Real-Time Notifications in Blazor WebAssembly with Azure SignalR Service

The Scenario

You are building a Blazor WebAssembly app that needs real-time notifications (status updates, alerts, chat messages, etc.) and it’s hosted on a multi-instance Azure App Service. For this setup, I recommend using Azure SignalR Service instead of standard SignalR for better scalability and reliability.

Key Requirements

  • Real-time notifications in a multi-instance environment
  • User-specific notifications (when user X triggers an activity, only user X receives the related notifications)
  • Secure identification using Azure AD Object ID (Oid) through a custom IUserIdProvider

This approach ensures your real-time functionality scales properly across multiple instances while maintaining security and user-specific targeting.

HTTP VS WEB-SOCKETS

HTTP and WebSocket are both ways for computers to talk to each other, but they work in different ways.

  • HTTP is used for simple requests, where a client sends a request and the server replies, then the connection is closed.
  • WebSocket keeps the connection open, allowing for real-time web functionality. Real-time web functionality is the ability to have server code push content to connected clients instantly as it becomes available, rather than having the server wait for a client to request new data, making it great for things like live chats or online games where constant updates are needed.

SignalR uses the new WebSocket transport where available and falls back to older transports where necessary. Using SignalR means that a lot of the extra functionality needed to implement web sockets is already done. SignalR also shields developers from having to worry about updates to WebSocket, since SignalR is updated to support changes in the underlying transport, providing the application a consistent interface across versions of WebSocket.

Do you need SignalR?

Use SignalR if the following apply to you:

✅ Users need to refresh pages to see new data,
✅ Pages implements long polling to retrieve new data
✅ Collaborative applications (such as simultaneous editing of documents)
✅ Job progress updates
✅ Application requires high frequency updates from the server

STANDARD SIGNALRSTANDARD + REDISAZURE SIGNALR
CONNECTION STORAGELocal server memoryLocal + Redis syncAzure service
SCALINGSingle server onlyMulti-serverAutomatic
SETUP COMPLEXITYSimpleComplex (Redis setup)Simple
CONNECTION MANAGEMENTSelf handledSelf + Redis handleAzure handles
MESSAGE ROUTINGLocal onlyVia RedisVia Azure

Why use Azure SignalR for real-time notifications in a multi-instance environment?

Multiple Users, Multiple Server Instances – No Problem!

Server 1
Server 2 → Azure SignalR Service ← Handles ALL user connections
Server 3

All instances can message any user through Azure
No Redis backplane needed

Azure SignalR Service:
├── Connection Manager → Tracks all WebSocket connections
├── Message Router → Routes messages to correct connections
├── Scale Units → Handles thousands of concurrent connections
└── Geographic Distribution → Multiple regions

Provision Azure SignalR Service via Terraform

main-signal.tf

Define a Hub

TaskHub.cs

Establish single user notifications

OidUserIdProvider.cs

Create an Azure SignalR service

AzureSignalRService.cs

Register in the program.cs

Why register as Singleton?

  • Singleton – One instance of a resource, reused anytime it’s requested.
  • Scoped – One instance of a resource, but only for the current request. New request (i.e. hit
    an API endpoint again) = new instance
  • Transient – A different instance of a resource, every-time it’s requested.

Register the backend service as a singleton as ensures that the hub context created once and reused forever. This leads to better performance (no recreation overhead), lower memory usage and no user-specific state to worry about.In general, SignalR hub contexts are designed to be long-lived.

If you register it as scoped then, it will quite expensive as a hub context would be recreated per request, leading to unnecessary disposal/recreation cycles, higher memory pressure and lower response times due to initialization costs.

Overall, this service is nothing more than a message sender, not a connection manager: Each user has their own WebSocket
connection to Azure SignalR and this mapping happens in Azure SignalR, not in this service!

Frontend

TaskNotificationService.cs

This service is registered as scoped on the frontend as there are user-specific subscriptions, user-specific connections and user-specific event handling.

Weather.razor

Conclusion

Whilst you could use Redis backplane to handle the multi-instance App Service self-hosted SignalR, I chose to be lazy and went with Azure SignalR instead. Like my former professor would say, “a lazy programmer is a good programmer”.

So in the end, the flow becomes:

  1. The Blazor WASM app authenticates with Azure AD and receives an access token containing the user’s Oid.
  2. The client establishes a SignalR connection to Azure SignalR Service, passing the Azure AD token.
  3. Azure SignalR Service routes the connection to one of the App Service instances.
  4. The backend uses OidUserIdProvider to extract the OID from the token and map the connection to the user.
  5. Backend services (e.g., triggered by webhooks or events) send notifications via IHubContext, targeting users by OID.
  6. Notifications are routed through Azure SignalR Service and pushed to the correct client(s).
  7. This architecture guarantees secure, scalable, and user-specific real-time notifications for your Blazor WebAssembly application.

In case you need a visual diagram – here’s what Claude thinks

1 thought on “Real-Time Notifications in Blazor WebAssembly with Azure SignalR Service”

  1. Pingback: Implementation and deployment of Azure AD App-to-App Authentication Between Azure Functions and Blazor WebAssembly – Azure, C#, Terraform, YAML

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top