Using real-time data in Angular with SignalR
Tags: angular, webdev
Published: 2024-02-29 15:15:23

**Written by [Lewis Cianci
](https://blog.logrocket.com/author/lewiscianci/)✏️**

It wasn’t too long ago that webpages worked entirely on static data. If you visited a webpage, it would load, and all of its data would only update when you refreshed the page in question. 

These days, such an approach is hardly sufficient. Your users expect to be notified in real-time and won’t tolerate waiting seconds or minutes between page loads to be updated on something happening. Instant messaging, price checkers, and many other tools rely on real-time notifications. 

Fortunately, if you’re a .NET developer with some Angular knowledge, implementing real-time messaging can be a fairly straightforward exercise. So, let’s explore how to use real-time data in Angular with SignalR. The full code for our demo app is [available on GitHub](https://github.com/azimuthdeveloper/angularsignalr/tree/master).

## Why should we use .NET and Angular for this problem?

.NET is a mature and stable server-side solution with many benefits. For example, it:

*   Is the right price (free!)
*   Only improves from each major version to the next
*   Is available on pretty much any platform you can think of
*   Has fantastic editor support by way of Visual Studio or JetBrains Rider
*   Enables you to build and deploy a simple app by compiling your code down to an executable and then just shipping that by itself

Likewise, Angular is also a stable and mature framework, but for the frontend. Microsoft has moved into this space with Blazor, but many developers still prefer Angular — or learned it before Blazor came on the scene. Angular also has excellent frontend UI frameworks like Angular Material. 

Angular has also added new functionality recently by way of Signals, which can help with the reactivity of our application. There's the possibility of confusion between [signals in Angular](https://blog.logrocket.com/angular-signals-vs-observables/) and SignalR in .NET, so we’ll need to delineate between both technologies, as we will do in this article.

## How does SignalR work?

SignalR works by using a variety of real-time connection methods to deliver notifications to applications. 

Before SignalR, if you were a .NET developer and wanted to utilize real-time messaging, you had to decide whether you wanted to use WebSockets, [server-side events](https://blog.logrocket.com/server-side-rendering-angular-16/), or HTTP long polling. If a client didn’t support one, you would have to implement it yourself. This added a lot of developmental burden. 

SignalR automatically chooses a connection method for you based on what the client supports. Normally, it will favor WebSockets, but can fall back to server-side events or HTTP long polling. SignalR also supports automatic reconnections, so even if your connection is flaky, it will handle reconnections for you.

## Our sample real-time Angular app: Food ordering

Real-time messaging has a raft of possible uses. But today, we’re going to use it to write a simple app meant for use by staff in a restaurant. 

In a restaurant, some staff attend to the front of the house, where the customers are, while others work in the kitchen. The waitstaff needs to put orders through while the kitchen staff needs to see those orders. At the same time, the kitchen staff should be able to update orders as they progress through the kitchen and out onto the table. 

Our finished product will look like this for the waitstaff: ![Preview Of The Waitstaff-Facing Page Of The Finished Real Time Angular App Built With Signalr. User Interface Includes Field To Input Table Number And Buttons To Order Items From Menu](https://blog.logrocket.com/wp-content/uploads/2024/02/img1-Preview-finished-real-time-app-waitstaff.png) Meanwhile, the kitchen staff will be able to look at their specific kitchen page to see what orders have come through: ![Preview Of The Kitchen-Staff-Facing Page Of The Finished Real Time Angular App Built With Signalr. User Interface Shows List Of Active Orders With Dropdowns. Each Dropdown Displays Ordered With A Dropdown Indicator](https://blog.logrocket.com/wp-content/uploads/2024/02/img2-Preview-finished-real-time-app-kitchen-staff.png) Visually, the app is _extremely underwhelming_. It almost hurts me to produce something that looks this drab. But real-time messaging is a complex topic in itself! So we really need to focus on that and leave aesthetics at the door. 

Okay, let’s dig in.

## Creating the server-side app

The first thing we’ll need to do is create a new .NET app that uses the `webapi` template. Open up the folder where you’ll be working and type the following command:

```bash
dotnet new webapi -n FoodOrdering
```

After a few moments, your new project should be created. 

First up, we’ll need to install some NuGet packages to our project to support what we’re doing. Install the following NuGet packages:

```c# 
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Sqlite
```

Next, we need to create an appropriate data model to house our food orders. Our orders will be transferred in real-time from server to client, but they will also be stored. This means if our app crashes or restarts, all of the orders will be stored for reuse when the app starts up again. 

Within the `Model/FoodItem.cs` file, our model will look like this:

```c# 
public class FoodItem
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string ImageUrl { get; set; }
}

public class FoodList
{
    public List<FoodItem> Items { get; set; }
}
```

Also, we’ll need a model to define what our food orders look like. This time, we’ll set up this model in the `Model/Order.cs` file:

```c# 
public class Order
{
    public int Id { get; set; }
    public int TableNumber { get; set; }
    public int FoodItemId { get; set; }
    public FoodItem FoodItem { get; set; }
    public DateTimeOffset OrderDate { get; set; }
    public OrderState OrderState { get; set; }
}

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum OrderState
{
    Ordered,
    Preparing,
    AwaitingDelivery,
    Completed
}
```

Now would be a good time to create a context in which to store the food and orders. Let’s create a data context for our application that will be used by [Entity Framework](https://learn.microsoft.com/en-us/aspnet/entity-framework) in the `Contexts/DataContext.cs` file:

```c# 
public class DataContext : DbContext
{
    public DataContext(DbContextOptions<DataContext> options): base(options)
    {

    }
    public DbSet<FoodItem> FoodItems { get; set; }
    public DbSet<Order> Orders { get; set; }
}
```

Next, run the `dotnet ef migrations add initial` command within our project to produce our initial set of database migrations. We won’t apply these just yet, however.

## Creating the seeding worker

Our application isn’t of much use to us right now. If we start it up, we’ll see that it has zero food items in it, so we can’t order anything or test that it works. We could manually load data into the test database, but this would prove irritating over time. 

Instead, let’s create a simple worker that runs with our app, ensures our database is in a useful state, and also populates it with some useful data. In our case, it will be a `BackgroundService` that we’ll call on a little later on. 

For now, our background service in the `Workers/SeedingWorker.cs` file looks like this:

```c# 
public class SeedingWorker : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;

    public SeedingWorker(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await SeedDataAsync();
    }

    private async Task SeedDataAsync()
    {
        using var scope = _scopeFactory.CreateScope();
        await using var context = scope.ServiceProvider.GetRequiredService<DataContext>();
        await context.Database.EnsureDeletedAsync();
        await context.Database.MigrateAsync();
        var foodItems = new List<FoodItem>
        {
            new FoodItem
            {
                Name = "Pizza",
                Description =
                    "A savory dish of Italian origin consisting of a usually round, flattened base of leavened wheat-based dough topped with tomatoes, cheese, and often various other ingredients (such as anchovies, mushrooms, onions, olives, pineapple, meat, etc.), which is then baked at a high temperature, traditionally in a wood-fired oven.",
                ImageUrl = "https://images.unsplash.com/photo-1513104890138-7c749659a591?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
            },
            new FoodItem
            {
                Name = "Sushi",
                Description =
                    "A Japanese dish of prepared vinegared rice (sushi-meshi), usually with some sugar and salt, accompanied by a variety of ingredients (neta), such as seafood, often raw, and vegetables.",
                ImageUrl = "https://plus.unsplash.com/premium_photo-1670333291474-cb722ca783a5?q=80&w=500&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
            },
            new FoodItem
            {
                Name = "Hamburger",
                Description =
                    "A sandwich consisting of one or more cooked patties of ground meat, usually beef, placed inside a sliced bread roll or bun. The patty may be pan fried, grilled, or flame broiled. Hamburgers are often served with cheese, lettuce, tomato, onion, pickles, bacon, or ketchup, mayonnaise, mustard, and other condiments.",
                ImageUrl = "https://images.pexels.com/photos/1639565/pexels-photo-1639565.jpeg?auto=compress&cs=tinysrgb&w=500&h=750&dpr=1"
            },
            new FoodItem()
            {
                Name = "Salad",
                Description = "The most amazing salad that has ever passed your lips.",
                ImageUrl = "https://images.pexels.com/photos/1059905/pexels-photo-1059905.jpeg?auto=compress&cs=tinysrgb&w=500&h=750&dpr=1"
            }
            // Add more food items as needed
        };

        await context.FoodItems.AddRangeAsync(foodItems);
        await context.SaveChangesAsync();
        Console.WriteLine("Seeding complete!");
    }
}
```

## Configuring the controllers

To support the functions of the kitchen staff and the waitstaff, we’ll create two controllers in the `Controllers` directory. We’ll use `FoodController.cs` and `KitchenController.cs` as the names for our controller files. 


Why do we even need controllers in a SignalR application? It’s a good question. SignalR can emit data in real-time, which has its use. However, we also need to retrieve data when the web page loads. In that context, it makes sense to get the list of items from the API, and then receive future updates to the data via SignalR. 

Our food controller will just get a list of food items, like so:

```c# 
[ApiController]
[Route("api/[controller]/[action]")]
public class FoodItemsController : ControllerBase
{
    private readonly DataContext _context;

    public FoodItemsController(DataContext context)
    {
        _context = context;
    }

    [HttpGet]
    public async Task<IActionResult> GetFoodItems()
    {
        var foodItems = await _context.FoodItems.ToListAsync();
        return Ok(foodItems);
    }
}
```

Our kitchen controller will return a list of orders that are currently not completed, like so:

```c# 
[ApiController]
[Route("api/[controller]/[action]")]
public class KitchenController
{
    private readonly DataContext _context;

    public KitchenController(DataContext context)
    {
        _context = context;
    }

    [HttpGet]
    public List<Order> GetExistingOrders()
    {
        var orders = _context.Orders.Include(x => x.FoodItem).Where(x => x.OrderState != OrderState.Completed);
        return orders.ToList();
    }
}
```

## Setting up the SignalR hub

SignalR runs on the idea of hubs. Clients can connect to these hubs to receive real-time information. The basic concept is that you can define functions on the hub that your client can invoke, but your server can also invoke functions on the client. 

Mentally mapping out what calls what and where can quickly become taxing. In these times, it pays to step through what we’re trying to accomplish before diving in. In the case of food orders, it can look something like this:

*   **Food ordering process:** Add the new order to the database
*   **Food update process:** Check that the order is in the system. If it is, update the order state
*   **In both cases:** Save the changes to the database and notify all connected clients of the new data

Let’s make this hub a reality in the `Hubs/FoodHub.cs` file:

```c# 
public class FoodHub : Hub<IFoodOrderClient>
{
    private readonly DataContext _context;

    public FoodHub(DataContext context)
    {
        _context = context;
    }

    public async Task OrderFoodItem(FoodRequest request)
    {
        _context.Orders.Add(new Order()
        {
            FoodItemId = request.foodId,
            OrderDate = DateTimeOffset.Now,
            TableNumber = request.table,
            OrderState = OrderState.Ordered,
        });

        await _context.SaveChangesAsync();
        await EmitActiveOrders();
    }

    public async Task UpdateFoodItem(int orderId, OrderState state)
    {
        var order = await _context.Orders.FindAsync(orderId);
        if (order != null)
        {
            order.OrderState = state;
        }

        await _context.SaveChangesAsync();
        await EmitActiveOrders();
    }

    public async Task EmitActiveOrders()
    {
        var orders = _context.Orders.Include(x => x.FoodItem).Where(x => x.OrderState != OrderState.Completed).ToList();

        await Clients.All.PendingFoodUpdated(orders);
    }

    public override async Task OnConnectedAsync()
    {
        Console.WriteLine(Context.ConnectionId);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception ex)
    {
        Console.WriteLine(Context.ConnectionId);
        await base.OnDisconnectedAsync(ex);
    }
}

// These are the RPC calls on the client
public interface IFoodOrderClient
{
    Task PendingFoodUpdated(List<Order> orders);
}
```

The `IFoodOrderClient` interface feels weird on the `Hub` method here, considering we don’t implement anything from this interface. However, these are the functions that we can call on the client, which we’ll see a little later on.

## Configuring the `Program.cs` file

Now, let's go ahead and set up our application for startup in the `Program.cs` file:

```c# 
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<DataContext>(options => options.UseSqlite("Data Source=mydatabase.sqlite"));
// Add the seeding worker
builder.Services.AddHostedService<SeedingWorker>();
// Configure the pipeline to accept enums as strings, instead of just numbers
builder.Services.AddMvc().AddJsonOptions(x =>
{
    x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
// Add SignalR
builder.Services.AddSignalR();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseRouting();
app.MapControllers();
// Configure our SignalR hub
app.MapHub<FoodHub>("/foodhub");

app.Run();
```

Here, we integrate a SQLite database to manage our order data, [Swagger to simplify API documentation and testing](https://blog.logrocket.com/documenting-express-js-api-swagger/), and SignalR to enable live updates.

## Setting up our client app

Our client app will be written in Angular 17\. This [version of Angular brings exciting changes](https://blog.logrocket.com/exploring-angular-evolution/), such as control flow syntax and real-time messaging systems such as Signals. To get started, navigate to a folder that’s just one level up from your .NET project, and type the following command:

```bash
ng new FoodOrderingClient
```

Because our project will need to receive food items that align with what we have on the server, we’ll first need to create our data model. In the `model/data.ts` file, we can set up our data model accordingly:

```typescript
export interface FoodItem {
  id: number;
  name: string;
  description: string;
  imageUrl: string;
}

export interface FoodList {
  items: FoodItem[];
}

export interface Order {
  id: number;
  tableNumber: number;
  foodItemId: number;
  foodItem: FoodItem;
  orderDate: Date; // Using Date for DateTimeOffset
  orderState: OrderState;
}

export enum OrderState {
  Ordered = 'Ordered',
  Preparing = 'Preparing',
  AwaitingDelivery = 'AwaitingDelivery',
  Completed = 'Completed'
}

export interface FoodRequest{
  table: number,
  foodId: number,
}
```

### Setting up our real-time service

Now, let’s create our real-time service, which will be responsible for connecting to our SignalR instance and reconnecting to the SignalR hub if the connection is dropped. It‘s also responsible for mapping the responses out of SignalR into something that makes more sense to Angular, like a standard observable. 

This part of the setup can get a little tricky, so we’ll take it slow and explain how it works. First, run the following command to get the Angular CLI to produce a service for us to use:

```bash
ng generate service RealtimeClient
```

Next, let’s configure it to work in our project. Our service will contain three properties:

*   One that references our SignalR hub
*   A second that houses our `Subject`
*   A third our `Observable` that other parts of our application can subscribe to

Let’s set these up now:

```typescript
private hubConnection: signalR.HubConnection;
private pendingFoodUpdatedSubject = new Subject<Order[]>();
ordersUpdated$: Observable<Order[]> = this.pendingFoodUpdatedSubject.asObservable();
```

Our constructor for this service will be responsible for starting the connection to the hub on the SignalR server, logging an error if the connection fails, and also retrying the connection if it drops out for any reason. 

Also, remember the functions we defined in the `IFoodOrderClient` in the Web API project? We also listen to when these functions are invoked on the server and handle the output here. When the `PendingFoodUpdated` function is called, the newly ordered food is submitted to the `pendingFoodUpdatedSubject`:

```typescript
constructor() {
  this.hubConnection = new signalR.HubConnectionBuilder()
    .withUrl('http://localhost:4200/foodhub') // Replace with your SignalR hub URL
    .build();

  this.hubConnection
    .start()
    .then(() => console.log('Connected to SignalR hub'))
    .catch(err => console.error('Error connecting to SignalR hub:', err));

  this.hubConnection.on('PendingFoodUpdated', (orders: Order[]) => {
    this.pendingFoodUpdatedSubject.next(orders);
  });
}
```

Finally, we’ll write two functions that invoke the SignalR functions to handle new orders being placed and updated accordingly. We can’t generate these invocations automatically, so we have to be careful to use the right parameters and types when we invoke these functions:

```typescript
async orderFoodItem(foodId: number, table: number) {
  console.log("ordering");
  await this.hubConnection.invoke('OrderFoodItem', {
    foodId,
    table,
  } as FoodRequest);
}

async updateFoodItem(orderId: number, state: OrderState) {
  await this.hubConnection.invoke('UpdateFoodItem', orderId, state);
}
```

### The `customers` page

Now, we can create a `customers` page that the customer or waitstaff can see. 

The function is fairly simple: we want it to accept a table number and then allow the user to click on what items they would like to order. It’s also possible to view orders that are currently in, bearing in mind that completed orders will not be shown. 

Here’s the code:

```html
<h3>Welcome to <i>Excellent</i> Restaurant!</h3>
<i>Our menu is a little bit reduced at the moment. Please bear with us.</i>
<p>
  <b>What is the customers' table number?</b></p>
<input placeholder="Table Number" [(ngModel)]="tableNumber" type="number">
<p>
  <b>What would they like to eat?</b>
</p>

<div style="width: 100%; height: 100%; display: flex; flex-wrap: wrap; gap: 5px; padding-bottom: 100px">
  @for (food of availableFood(); track food.id) {
    <div style="width: 45%; margin: 0 10px; text-align: center; border: black 2px solid; border-radius: 10px"><img [src]="food.imageUrl" [alt]="food.description">
      <p>
        <i>{{ food.description }}</i>
      </p>
      <button [disabled]="!tableNumber" (click)="sendOrder(food.id, tableNumber!)">Order {{food.name}}</button>
    </div>
  }
</div>

@if (showActiveOrders){
<div style="position: fixed; background-color: rgba(59,189,168,0.3); top: 0; left: 0; right: 0; bottom: 0; z-index: 10">
  <div style="margin: 15%; background-color: white; height: 40%; width: 60%; border: black 2px solid; border-radius: 15px">
    <h3 style="text-align: center">Active orders</h3>
    <ul>
      @for (order of activeOrders(); track order){
        <li>{{order.foodItem.name}} for table {{order.tableNumber}}. Status: {{order.orderState}}</li>
      }
    </ul>
    <button (click)="showActiveOrdersToggle()">Hide Orders</button>
  </div>
</div>
}

<div style="height: 50px; background-color: rgba(86,157,238,0.8); width: 100%; position:fixed; bottom: 0; left:0; right: 0; display: flex; justify-content: center; align-content: center">
  <button (click)="showActiveOrdersToggle()">Show Active Orders</button>
</div>
```

Within the code for this component, we’ll have two dependencies: `RealtimeClientService` and `HttpClient`. The reason for this is that we want to receive real-time updates when they occur, but we also want to receive a list of static data, such as what food is available to order. 

We’ll also create two signals: one to contain the list of food that we can order and another that will update when the orders in the system have been updated:

```typescript
availableFood = signal<Array<FoodItem>>([]);
activeOrders = signal<Array<Order>>([]);
activeOrdersSubscription?: Subscription;

constructor(private realtime: RealtimeClientService, private http: HttpClient) {
}
```

Within our initialization function, we retrieve all available items to order from the API, along with all current orders from the API. We then subscribe to our real-time service to receive updates when order statuses have been updated:

```typescript
async ngOnInit() {
  let food = await firstValueFrom(this.http.get<Array<FoodItem>>('http://localhost:4200/api/FoodItems/GetFoodItems'));
  this.availableFood.set([...food]);

  let orders = await firstValueFrom(this.http.get<Array<Order>>('http://localhost:4200/api/Kitchen/GetExistingOrders'));
  this.activeOrders.set([...orders]);

  this.activeOrdersSubscription = this.realtime.ordersUpdated$.subscribe(orders => {
    this.activeOrders.set([...orders]);
  });
}
```

We also call the real-time service from our function that places the order:

```typescript
async sendOrder(foodId: number, tableNumber: number) {
  await this.realtime.orderFoodItem(foodId, tableNumber);
}
```

### The `kitchen` component

Our `kitchen` component is responsible for showing the list of orders, but it also has a critical role to play in this real-time opera. Namely, we want to enable kitchen staff to update order progress through the kitchen, and when orders are completed, we want to remove them from the display entirely. 

Create a new component and call it `kitchen`. The template will just iterate through a list of orders and show their current state. When the dropdown box is changed, however, we’ll trigger an event in our component to update the state appropriately, which we’ll see in a moment:

```html
<h2 style="text-align: center">Active Orders</h2>
<div style="width: 100%; height: 100%; flex-direction: row; display: flex; flex-wrap: wrap">
  @for (order of foodItems(); track order.id) {
    <div style="width: 200px; padding: 20px; border: solid 1px black">
      <p>
        Food: {{ order.foodItem.name }}
      </p>
      <p>
        Table number: {{ order.tableNumber }}
      </p>
      <p>
        Ordered at: {{ order.orderDate | date:'dd/MM/yyyy H:mm' }}
      </p>
      <select (change)="updateState(order.id, $event)">
        @for (state of foodStates; track state) {
          <option [value]="state">{{ state }}</option>
        }
      </select>
    </div>
  }
</div>
```

Within the component, we want to first receive a list of existing orders at initialization and use that list to populate the screen. Once we have this static data, we want to receive subsequent updates to this screen:

```typescript
async ngOnInit() {
  // Load exisiting orders (static data)
  let existingOrders = await firstValueFrom(this.http.get<Array<Order>>('http://localhost:4200/api/Kitchen/GetExistingOrders'));
  this.orders.set([...existingOrders]);
  /// Subscribe to future order updates
  this.orderSubscription = this.realtime.ordersUpdated$.subscribe(orders => this.orders.set([...orders]));
}
```

Finally, once the state of the order has been updated, we want to send that updated state to the server. This occurs when the `updateState` dropdown box is changed in the layout.

```typescript
async updateState(id: number, $event: Event) {
  let value = ($event.target as HTMLSelectElement)?.value; // Get the text from the control
  await this.realtime.updateFoodItem(id, value as OrderState); // Set the new enum value
}
```

### Configuring routes and the development proxy

We’re so close to seeing how this works, but first, we have to set up routing and configure the development proxy. Within our `app.routes.ts`, we just want to route the customers to the `customers` component and the kitchen to the `kitchen` component:

```typescript
export const routes: Routes = [
  { path: 'customers', component: CustomersComponent },
  { path: 'kitchen', component: KitchenComponent },
];
```

Within our `angular.json` file, we also want to configure our `serve` statement to load some configuration that will proxy our requests so our setup works in our local environment. Remember, JSON doesn’t have comments, so my comments here are just showing you where to look and should not be copied into your `angular.json` file:

```json
"serve": { // Look for the "serve" entry
  "options": { // Add options if it doesn't exist
    "proxyConfig": "proxy.conf.json" // Finally, set to proxy.conf.json
  },
```

The contents of our `proxy.conf.json` will redirect API or SignalR requests that occur within our app to the .NET app that we’ve made. 

If we tried to call the API directly without this proxy, our request would fail with a CORS error, as we tried to access a resource on a different host to our current app. We could work around this on the .NET end by permitting all CORS requests, but that could introduce a security problem. 

Let’s configure our `proxy.conf.json` like so:

```json
{
  "/foodhub": {
    "target": "http://localhost:5054",
    "secure": false,
    "changeOrigin": true,
    "logLevel": "debug",
    "ws": true
  },
  "/api":{
    "target": "http://localhost:5054",
    "secure": false,
    "changeOrigin": true,
    "logLevel": "debug"
  }
}
```

The main point of interest here is that, for our hub (which is SignalR), we need to tell the Angular CLI proxy to also proxy WebSocket requests so that SignalR still works correctly.

### Putting it all together

Now, let’s run our .NET app, and then run our Angular client app. Within our Angular app, let’s put in some orders for the people at table five: ![User Shown Interacting With Waitstaff-Facing Page Of Real Time Angular App To Input Table Number, Place Orders, And Scroll Up And Down](https://blog.logrocket.com/wp-content/uploads/2024/02/img3-Demo-using-real-time-app-place-order.gif) Now, if we navigate to the `kitchen` link, we can see that the orders are there, waiting for our kitchen staff to act on: ![Demo Screenshot Showing Six Placed Orders Visible On Kitchen Staff Side Of App With Table Numbers, Food Name, And Order Info](https://blog.logrocket.com/wp-content/uploads/2024/02/img4-Demo-placed-orders-visible-kitchen-staff-side-app.png) However, if more orders are added on the `customers` page, the orders come through automatically: ![Orders Being Added To Kitchen Staff Page. New Orders Are Appended To End Of List In Rows Of Three Items Per Row](https://blog.logrocket.com/wp-content/uploads/2024/02/img5-Orders-added-kitchen-staff-page.gif) Finally, if the kitchen updates the state, the state is also updated on the customer-facing screen. In the image below, the customer screen is on the left and the kitchen screen is on the right: ![Customer Facing And Kitchen Facing Pages Shown Side By Side To Show Synchronous Updates Occurring As User Interacts With Active Order Screen](https://blog.logrocket.com/wp-content/uploads/2024/02/img6-Synchronous-updates-customers-kitchen-pages.gif) And just like that, our orders are traveling from the server to our database, but are also being published over SignalR! 🎉🎉

## Adding authentication

There are countless methods of authentication available to use for web apps these days. However, in our demo app, we could use the example of making a simple token available in the HTTP header to permit access to the API. 

If our app were in use by a real restaurant, the kitchen page would be on a computer in the kitchen, and would not need to be secured. However, the customer ordering screen could be more within reach to the general public — so let's secure that side of the food ordering app. 

Even for our simple example, there are a few steps we need to follow. We need to provision a JWT token to people logging in, and then use that token in our Web API requests as well as our SignalR hub. We can use the same token for both, although it does require a little bit of massaging to get working.

### Authentication controller

First up, add an `AuthenticationController.cs` to our controllers. The responsibility of this controller is to take requests, and if they have a username of `admin` and a password of `password`, issue a token accordingly:

```c#
[ApiController]
[Route("api/[controller]/[action]")]
public class AuthenticationController : ControllerBase
{
    private readonly IConfiguration _configuration;

    public AuthenticationController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpPost]
    public async Task<IActionResult> Login(Authentication authentication)
    {
        if (authentication.Username == "admin" && authentication.Password == "password")
        {
            // Generate JWT token
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, authentication.Username)
            };
            var token = GenerateJwtToken(claims);

            return Ok(new { token });
        }
        else
        {
            return Unauthorized();
        }
    }

    private string GenerateJwtToken(List<Claim> claims)
    {
        var issuer = _configuration["Jwt:Issuer"];
        var audience = _configuration["Jwt:Audience"];
        var signingKey = Encoding.UTF8.GetBytes(_configuration["Jwt:SigningKey"]);

        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(30),
            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(signingKey), SecurityAlgorithms.HmacSha256Signature)
        );

        var tokenHandler = new JwtSecurityTokenHandler();
        return tokenHandler.WriteToken(token);
    }
}
```

### Updating application configuration

We’re reading from our `appsettings.json` file here, so we’ll need to add the following details to that file:

```json
... other configuration ...
"Jwt": {
  "Issuer": "FoodOrdering",
  "Audience": "FoodOrderingClient",
  "SigningKey": "$3cr3tkey$3cr3tkey$3cr3tkey$3cr3tkey$3cr3tkey$3cr3tkey$3cr3tkey$3cr3tkey$3cr3tkey"
}
```

The signing key has to be of a certain length, hence why it’s so long in this example.

### Updating application startup

Adding JWT token validation to our app makes the app startup quite a bit more complicated. Apart from adding authentication and authorization to to our services, we also need to configure the authentication middleware to accept and utilize the provided tokens. 

Let’s load the provided configuration and use it to configure what tokens our app would accept:

```c#
var configuration = builder.Configuration;
var jwtIssuer = configuration.GetValue<string>("Jwt:Issuer");
var jwtAudience = configuration.GetValue<string>("Jwt:Audience");
var jwtSigningKey = configuration.GetValue<string>("Jwt:SigningKey");

// Configure JWT bearer authentication
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidIssuer = jwtIssuer,

        ValidateAudience = true,
        ValidAudience = jwtAudience,

        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSigningKey)),

        RequireExpirationTime = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero // Adjust if needed for server clock differences
    };
});
```

With that, our API access is done. But there’s a small wrinkle: we also use SignalR, and SignalR will tend to use WebSockets if it can. WebSockets don’t really behave like normal HTTP requests, and they can’t really have a header authorization value updated. 

Fortunately, we can configure the JWT events manually, pass the token via a query string, and then load that into our hub for authorization:

```c#
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];

            // If the request is for our hub...
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) &&
                (path.StartsWithSegments("/foodhub")))
            {
                // Read the token out of the query string
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
```

WIth this small change, we can now set `[Authorize]` attributes for all the controllers we want to secure. In our case, we’ll be securing the `FoodController` controller, so the list of food isn’t loaded without authentication. We’ll also secure the `OrderFoodItem` method on the SignalR hub.

## Client-side configuration

We have a few changes to make in our client application. We need to handle the user logging in, and if login is successful, set the token to be used within our application on subsequent requests. We can do this by setting up an interceptor to “intercept” HTTP requests and attach the token as required. 

The actual workings of this are fairly simple. Retrieve the token, and then set the `Authorization` header to the retrieved value:

```typescript
@Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = sessionStorage.getItem('token');
    const bearerToken = `Bearer ${token}`;
    const authReq = req.clone({
      headers: req.headers.set('Authorization', bearerToken),
    });
    return next.handle(authReq);
  }
}
```

For our app, we want the interceptor to be used all the time. So, it makes sense to update our `app.config.ts` file to include this interceptor:

```typescript
export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes),
    // Add the HTTP Client with interceptor
    provideHttpClient(withInterceptorsFromDi()),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthenticationInterceptor,
      multi: true
    }]
}
```

### Updating SignalR to use the token

Because we can’t connect our SignalR hub when the app starts now, we have to connect it when the user has logged in. That means we have to move our main hub startup logic into a `connect` method that we can call after login. We also have to retrieve the token from storage and set it, if it has been set:

```typescript
connect(){
  this.hubConnection = new signalR.HubConnectionBuilder()
    .withUrl('http://localhost:4200/foodhub', {
      withCredentials: sessionStorage.getItem('token') != null,
      accessTokenFactory: () => {
        let token = sessionStorage.getItem('token');
          return token ?? '';
      },
      skipNegotiation: true,
      transport: signalR.HttpTransportType.WebSockets,
    }) // Replace with your SignalR hub URL
    .build();

  this.hubConnection
    .start()
    .then(() => console.log('Connected to SignalR hub'))
    .catch(err => console.error('Error connecting to SignalR hub:', err));

  this.hubConnection.on('PendingFoodUpdated', (orders: Order[]) => {
    this.pendingFoodUpdatedSubject.next(orders);
  });
}
```

### Updating the customer-facing ordering page

If the user isn’t logged in, we want to show the login dialog. Once they have logged in, we want to store the token, and allow them to interact with the app. 

Within the customers component, we can design a simple login dialog for the user:

```typescript
@if (needsLogin){
  <div class="overlay">
    <div class="overlay-inner" style="display: flex; flex-direction: column; gap: 10px; padding: 20px;">
      <h3>
        Login
      </h3>
      <input [(ngModel)]="login">
      <input [(ngModel)]="password" type="password">
      <button (click)="doLogin(login, password)">Login</button>
    </div>
  </div>
}
```

This login dialog should look like so: ![Login Dialog Open To Demonstrate Authentication For Protected Route — Specifically, Customer Facing Page — In Real Time Angular App](https://blog.logrocket.com/wp-content/uploads/2024/02/img7-Login-dialog-authentication-protected-route-real-time-app.png) When the **Login** button is pressed, the credentials will be checked against the server. If they match, a token will be stored. Otherwise, the user will be told to try again:

```typescript
async doLogin(login?: string, password?: string) {
  if (login && password) {
    try {
      let response = await firstValueFrom(this.http.post<AuthenticationResponse>('http://localhost:4200/api/Authentication/Login', {
        username: login,
        password: password
      }));
      // debugger;
      sessionStorage.setItem("token", response.token);
      this.needsLogin = false;
      // Authentication successful, continue to load the food menu and connect the SignalR hub.
      await this.loadOrders();
      this.realtime.connect();
    } catch (e) {
      alert("Incorrect username/password");
    }
  }
}
```

So, let’s see how this looks by launching the app across two browsers. As we set up, the user can log in to place orders, and orders are still sent to the kitchen, where users are not required to log in: ![Demo Of Authenticated Page Used To Place Orders While Unprotected Kitchen Facing Page Can Be Used Without Requiring Login](https://blog.logrocket.com/wp-content/uploads/2024/02/img8-Demo-authentication-place-orders-send-kitchen-staff-page-no-login-required.gif)

## Conclusion

Real-time messaging techniques like SignalR, when paired with excellent frontend frameworks like Angular, can help developers create some high-quality apps that are reactive to user inputs. 

Recent developments in Angular such as Signals only aid in how easy it can be to make a responsive app. Some configuration aspects can be non-intuitive, like how to set up authentication, but hopefully, this guide has helped you down the right path. 

The [full code sample is available on GitHub](https://github.com/azimuthdeveloper/angularsignalr/tree/master). Happy real-time app development!

---

## Experience your Angular apps exactly how a user does

Debugging Angular applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Angular state and actions for all of your users in production, [try LogRocket](https://lp.logrocket.com/blg/angular-signup).

[![LogRocket Signup](https://files.readme.io/610d6a7-687474703a2f2f692e696d6775722e636f6d2f696147547837412e706e67.png)](https://lp.logrocket.com/blg/angular-signup)

[LogRocket](https://lp.logrocket.com/blg/angular-signup) is like a DVR for web and mobile apps, recording literally everything that happens on your site including network requests, JavaScript errors, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket NgRx plugin logs Angular state and actions to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Angular apps — [start monitoring for free](https://lp.logrocket.com/blg/angular-signup).