Real‑time Data Sync: The Challenge
Imagine a collaborative dashboard that reflects every user action within milliseconds. In finance, a stock‑trading UI must display order book updates instantly, otherwise traders lose money. Achieving sub‑second consistency across web clients is no longer a nice‑to‑have; it is a competitive necessity. .NET 8 Minimal APIs, Azure Cosmos DB Change Feed, and SignalR together form a low‑latency pipeline that can satisfy this demand without building a custom message broker.
Why .NET 8 Minimal APIs Fit the Scenario
Minimal APIs strip away the ceremony of traditional MVC controllers, letting you define routes with a single line of code. The runtime overhead drops by roughly 15 % compared with full controller pipelines, according to Microsoft’s performance benchmarks for .NET 8. This lean model is ideal for a thin HTTP layer that only forwards change notifications to connected browsers.
Setting Up a .NET 8 Minimal API Project
Start with the .NET SDK 8.0 (released November 2023). The following commands create a clean workspace:
dotnet new web -n RealTimeSyncDemo --framework net8.0
cd RealTimeSyncDemo
dotnet add package Microsoft.Azure.Cosmos
dotnet add package Microsoft.AspNetCore.SignalR
dotnet add package Microsoft.Azure.Cosmos.ChangeFeedProcessorNext, open Program.cs and register the required services. The code below shows a concise configuration:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(sp =>
new CosmosClient(builder.Configuration["Cosmos:ConnectionString"]));
builder.Services.AddSignalR();
var app = builder.Build();
app.MapHub("/changes");
app.MapGet("/health", () => Results.Ok("OK"));
app.Run(); Notice the use of AddSingleton—the client is thread‑safe and should be reused throughout the app’s lifetime.
Connecting to Azure Cosmos DB and Enabling Change Feed
Cosmos DB’s Change Feed streams every insert, replace, or delete operation in the order they occur. To consume it, register a ChangeFeedProcessor that points to a dedicated lease container. The following snippet configures the processor inside a hosted service:
public class FeedProcessorHostedService : IHostedService
{
private readonly CosmosClient _client;
private ChangeFeedProcessor _processor;
public FeedProcessorHostedService(CosmosClient client) => _client = client;
public async Task StartAsync(CancellationToken ct)
{
var source = _client.GetContainer("DemoDb", "Items");
var lease = _client.GetContainer("DemoDb", "Leases");
_processor = source.GetChangeFeedProcessorBuilder- ("processor", async (changes, token) =>
{
foreach (var item in changes)
{
// Broadcast each change via SignalR
await HubContext.Clients.All.SendAsync("ItemChanged", item, token);
}
})
.WithInstanceName("real‑time-sync-instance")
.WithLeaseContainer(lease)
.Build();
await _processor.StartAsync();
}
public async Task StopAsync(CancellationToken ct) => await _processor.StopAsync();
}
builder.Services.AddHostedService
(); The processor guarantees at‑least‑once delivery, and the lease container ensures multiple instances can share the workload without duplicate messages.
Broadcasting Changes with SignalR
SignalR abstracts WebSocket, Server‑Sent Events, and Long Polling behind a simple hub API. Clients subscribe to a method name—here ItemChanged—and receive a strongly typed payload. The hub definition is trivial:
public class ChangesHub : Hub
{
// No server‑side methods needed for push‑only scenario
}On the browser side, a JavaScript snippet connects and updates the UI:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/changes")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on("ItemChanged", item => {
// Example: refresh a table row or add a new card
updateDashboard(item);
});
connection.start().catch(err => console.error(err));This pattern eliminates polling loops, reduces network traffic by up to 80 % compared with a 5‑second poll interval, and keeps the UI responsive.
Putting It All Together – A Minimal End‑to‑End Example
The following consolidated Program.cs shows the complete flow from HTTP request to real‑time broadcast. Replace the placeholder connection string with your Azure portal value.
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.json");
builder.Services.AddSingleton(sp =>
new CosmosClient(builder.Configuration["Cosmos:ConnectionString"]));
builder.Services.AddSignalR();
builder.Services.AddHostedService();
var app = builder.Build();
app.MapHub("/changes");
app.MapPost("/items", async (Item item, CosmosClient client) =>
{
var container = client.GetContainer("DemoDb", "Items");
await container.CreateItemAsync(item);
return Results.Created($"/items/{item.id}", item);
});
app.Run();
public record Item(string id, string name, double price);
When a POST request creates a new Item, Cosmos DB writes the document, the Change Feed picks it up, and the hosted processor pushes the JSON payload to every connected browser instantly.
Performance Tips and Common Pitfalls
1. **Tune the lease container RU/s** – Under‑provisioned leases cause the processor to lag. Microsoft recommends allocating at least 400 RU/s for a modest workload (≈1 000 changes per second).
2. **Enable TCP keep‑alive** on the SignalR client to avoid silent disconnections in mobile browsers.
3. **Avoid large payloads** – Serialize only the fields the UI needs; a 5 KB document can double the bandwidth when streamed to 10 000 clients. Use JsonSerializerOptions with IgnoreNullValues to trim the JSON.
4. **Handle duplicate events** – Because the Change Feed guarantees at‑least‑once delivery, your UI logic should be idempotent (e.g., upsert into a local map keyed by id).
Conclusion
By combining .NET 8 Minimal APIs, Azure Cosmos DB Change Feed, and SignalR, developers can construct a scalable, low‑latency pipeline without introducing a separate message broker. The approach leverages native Azure services, keeps the codebase under 150 lines, and delivers sub‑second updates to thousands of browsers. Start with the minimal project template, enable the Change Feed, and let SignalR do the heavy lifting—your real‑time features will be production ready in days, not weeks.
Sources
Microsoft Docs – Azure Cosmos DB Change Feed
Microsoft Docs – SignalR for ASP.NET Core
Microsoft Docs – .NET 8 Minimal APIs Performance Guide
Author: Mahmut Sarıkaya — sarikayadev.com