Set up Azure SignalR Service in the AppHost
This article is the reference for the Aspire Azure SignalR Service hosting integration. It enumerates the AppHost APIs — with examples for both AppHost.cs and apphost.ts — that you use to model an Azure SignalR Service resource in your AppHost project.
If you’re new to the Azure SignalR Service integration, start with the Get started with the Azure SignalR Service integration guide. For how consuming apps read the connection information this page exposes, see Connect to Azure SignalR Service.
Installation
Section titled “Installation”To start building an Aspire app that uses Azure SignalR Service, install the 📦 Aspire.Hosting.Azure.SignalR NuGet package:
aspire add azure-signalrLearn more about aspire add in the command reference.
Or, choose a manual installation approach:
#:package Aspire.Hosting.Azure.SignalR@*<PackageReference Include="Aspire.Hosting.Azure.SignalR" Version="*" />aspire add azure-signalrLearn more about aspire add in the command reference.
This updates your aspire.config.json with the Azure SignalR Service hosting integration package:
{ "packages": { "Aspire.Hosting.Azure.SignalR": "13.3.0" }}Add Azure SignalR Service resource
Section titled “Add Azure SignalR Service resource”Once you’ve installed the hosting integration in your AppHost project, you can add an Azure SignalR Service resource as shown in the following examples:
var builder = DistributedApplication.CreateBuilder(args);
var signalR = builder.AddAzureSignalR("signalr");
var api = builder.AddProject<Projects.ApiService>("api") .WithReference(signalR) .WaitFor(signalR);
// After adding all resources, run the app...builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const signalR = await builder.addAzureSignalR("signalr");
await builder.addProject("api", "../ApiService/ApiService.csproj") .withReference(signalR) .waitFor(signalR);
// After adding all resources, run the app...await builder.build().run();Azure SignalR Service modes
Section titled “Azure SignalR Service modes”Azure SignalR Service supports different service modes that affect how clients and servers communicate:
- Default mode — client connections are proxied through the hub server. Traditional ASP.NET Core SignalR hubs work in this mode.
- Serverless mode — there is no hub server; clients negotiate directly with the service and servers communicate via the REST API or Management SDK.
For more information, see Azure SignalR Service modes.
Set service mode
Section titled “Set service mode”Pass the AzureSignalRServiceMode enum value when adding the resource:
using Aspire.Hosting.Azure;
var builder = DistributedApplication.CreateBuilder(args);
var signalR = builder.AddAzureSignalR( "signalr", AzureSignalRServiceMode.Serverless);
builder.AddProject<Projects.ApiService>("api") .WithReference(signalR) .WaitFor(signalR);
// After adding all resources, run the app...builder.Build().Run();The available modes are:
AzureSignalRServiceMode.Default— the default hub-server proxy mode.AzureSignalRServiceMode.Serverless— no hub server; clients negotiate directly with the service.
The TypeScript addAzureSignalR API doesn’t expose a service mode parameter. To configure Serverless mode in a TypeScript AppHost, use configureInfrastructure to modify the generated Bicep:
import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const signalR = await builder.addAzureSignalR("signalr");await signalR.configureInfrastructure(async (infra) => { // Use infra.getProvisionableResources() to access the SignalRService object // and set features[ServiceMode] = 'Serverless' on the Bicep resource.});
await builder.build().run();The Azure SignalR Service emulator only works in Serverless mode. The AddNamedAzureSignalR method (used for Default mode hub servers) does not support the emulator.
Add Azure SignalR Service emulator resource
Section titled “Add Azure SignalR Service emulator resource”The Azure SignalR Service emulator allows local development and testing without requiring an Azure subscription. The emulator only supports Serverless mode.
To use the emulator, chain a call to RunAsEmulator (or runAsEmulator):
using Aspire.Hosting.Azure;
var builder = DistributedApplication.CreateBuilder(args);
var signalR = builder.AddAzureSignalR("signalr", AzureSignalRServiceMode.Serverless) .RunAsEmulator();
builder.AddProject<Projects.ApiService>("api") .WithReference(signalR) .WaitFor(signalR);
// After adding all resources, run the app...builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const signalR = await builder.addAzureSignalR("signalr") .runAsEmulator();
await builder.addProject("api", "../ApiService/ApiService.csproj") .withReference(signalR) .waitFor(signalR);
// After adding all resources, run the app...await builder.build().run();The RunAsEmulator call configures the Azure SignalR Service resource to use the mcr.microsoft.com/signalr/signalr-emulator:latest container image for local development. The emulator is started when the AppHost runs and stopped when it stops.
Connect to an existing Azure SignalR instance
Section titled “Connect to an existing Azure SignalR instance”You might have an existing Azure SignalR Service that you want to connect to. Use AsExisting (or asExisting) to annotate that your AzureSignalRResource is an existing resource:
var builder = DistributedApplication.CreateBuilder(args);
var existingSignalRName = builder.AddParameter("existingSignalRName");var existingSignalRResourceGroup = builder.AddParameter("existingSignalRResourceGroup");
var signalR = builder.AddAzureSignalR("signalr") .AsExisting(existingSignalRName, existingSignalRResourceGroup);
builder.AddProject<Projects.ApiService>("api") .WithReference(signalR);
// After adding all resources, run the app...builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const existingName = await builder.addParameter("existingSignalRName");const existingRG = await builder.addParameter("existingSignalRResourceGroup");
const signalR = await builder.addAzureSignalR("signalr") .asExisting(existingName, { resourceGroup: existingRG });
await builder.addProject("api", "../ApiService/ApiService.csproj") .withReference(signalR);
await builder.build().run();For more information on treating Azure SignalR resources as existing resources, see Use existing Azure resources.
Provisioning-generated Bicep
Section titled “Provisioning-generated Bicep”When you add an Azure SignalR Service resource, Aspire generates provisioning infrastructure using Bicep. The generated Bicep includes defaults for location, SKU, and role assignments:
@description('The location for the resource(s) to be deployed.')param location string = resourceGroup().location
resource signalr 'Microsoft.SignalRService/signalR@2024-03-01' = { name: take('signalr-${uniqueString(resourceGroup().id)}', 63) location: location properties: { cors: { allowedOrigins: [ '*' ] } disableLocalAuth: true features: [ { flag: 'ServiceMode' value: 'Default' } ] } kind: 'SignalR' sku: { name: 'Free_F1' capacity: 1 } tags: { 'aspire-resource-name': 'signalr' }}
output hostName string = signalr.properties.hostName
output name string = signalr.nameThe preceding Bicep provisions an Azure SignalR Service resource. Additionally, role assignments are created in a separate module:
@description('The location for the resource(s) to be deployed.')param location string = resourceGroup().location
param signalr_outputs_name string
param principalType string
param principalId string
resource signalr 'Microsoft.SignalRService/signalR@2024-03-01' existing = { name: signalr_outputs_name}
resource signalr_SignalRAppServer 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(signalr.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '420fcaa2-552c-430f-98ca-3264be4806c7')) properties: { principalId: principalId roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '420fcaa2-552c-430f-98ca-3264be4806c7') principalType: principalType } scope: signalr}The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Direct edits to the Bicep file will be overwritten, so make changes through the C# provisioning APIs.
Customize provisioning infrastructure
Section titled “Customize provisioning infrastructure”All Aspire Azure resources are subclasses of the AzureProvisioningResource type. This type enables customization of the generated Bicep through the ConfigureInfrastructure API:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureSignalR("signalr") .ConfigureInfrastructure(infra => { var signalRService = infra.GetProvisionableResources() .OfType<SignalRService>() .Single();
signalRService.Sku.Name = "Premium_P1"; signalRService.Sku.Capacity = 10; signalRService.PublicNetworkAccess = "Enabled"; signalRService.Tags.Add("ExampleKey", "Example value"); });
// After adding all resources, run the app...builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
await builder.addAzureSignalR("signalr") .configureInfrastructure(async (infra) => { // Use infra to customize the generated Bicep });
await builder.build().run();The preceding C# code:
- Retrieves the single
SignalRServiceresource from the provisionable resources. - Sets the SKU name to
Premium_P1and capacity to10. - Enables public network access.
- Adds a tag with a key of
ExampleKeyand a value ofExample value.
For more information, see Azure.Provisioning customization.
Connection properties
Section titled “Connection properties”When you reference an Azure SignalR Service resource using WithReference (or withReference), the following connection property is made available to the consuming project:
| Property Name | Description |
|---|---|
Uri | The endpoint URI for the SignalR service, with the format https://{host} in Azure (typically https://<resource-name>.service.signalr.net) or the emulator-provided endpoint when running locally |
Aspire exposes each property as an environment variable named [RESOURCE]_[PROPERTY]. For instance, the Uri property of a resource called signalr becomes SIGNALR_URI.
Additionally, Aspire sets ConnectionStrings__signalr (which maps to ConnectionStrings:signalr in .NET configuration), used by AddNamedAzureSignalR in Default mode hub server projects.
For the full reference of connection properties and per-language connection examples, see Connect to Azure SignalR Service.
Hosting integration health checks
Section titled “Hosting integration health checks”The Azure SignalR Service hosting integration doesn’t register a container-based health check when connecting to the cloud service. When running the emulator locally, Aspire monitors the container lifecycle to determine readiness.