Build Real-time Serverless Apps with Azure SignalR Service
Tags: azurefunctions, serverless, azure, websocket
Published: 2019-03-06 18:03:42

---
title: Build Real-time Serverless Apps with Azure SignalR Service
published: true
description: How to build a serverless, real-time, collaborative canvas using Azure Functions and Azure SignalR Service
tags: azurefunctions, serverless, azure, websocket
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ge27phuwfpjffznchndx.png
canonical_url: https://anthonychu.ca/post/serverless-realtime-java-azure-functions-signalr-service/
---

Today, Microsoft [announced](https://azure.microsoft.com/en-us/blog/real-time-serverless-applications-with-the-signalr-service-bindings-in-azure-functions/?WT.mc_id=devto-blog-antchu) the general availability of the Azure SignalR Service bindings for Azure Functions. The bindings allow Azure Functions to integrate seamlessly with SignalR Service to broadcast real-time messages at large scale over WebSockets.

We'll take a look at how to build a browser-based, collaborative drawing app using HTML and JavaScript. Multiple people can open the app in their own browser and draw on the canvas. Changes are synchronized in real-time using Azure Functions and SignalR Service.

Java language support in Azure Functions has also recently become [generally available](https://azure.microsoft.com/en-us/blog/announcing-the-general-availability-of-java-support-in-azure-functions/?WT.mc_id=devto-blog-antchu). In this article, we'll be writing our functions in Java, but the SignalR Service bindings also work with other languages supported by Azure Functions. Check out the [GitHub repo](https://github.com/anthonychu/serverless-draw) for source code in Java, C#, and JavaScript.

## Overview

There are three major components of the app: the drawing canvas, the Azure Function app, and Azure SignalR Service.

![Architecture](https://thepracticaldev.s3.amazonaws.com/i/49mhwtjpunxh01n4z5hw.png)

<!--more-->

1. The canvas is a simple JavaScript app that runs in the browser. When the app starts up, it retrieves the SignalR Service connection endpoint and access token from an Azure Function named "negotiate".

1. The app connects to SignalR Service and a real-time channel is established. It's usually a WebSocket connection, but SignalR will automatically fallback to other transports if WebSocket is not available for a given connection.

1. When lines are drawn on the canvas, the app sends a series of strokes to an Azure Function named "draw".

1. The draw Azure Function uses SignalR Service to broadcast the series of strokes to all connected clients.

## Create an Azure SignalR Service instance

Azure SignalR Service is a fully managed real-time messaging platform that our app will use to broadcast messages. We can create a **free** instance of the service in the Azure portal, or we can run the following Azure CLI command.

```bash
az signalr create -n $SIGNALR_NAME -g $GROUP_NAME --sku Free_DS2 -l westus
```

After the instance is created, we'll need to get the connection string that will be used by the function app.

```bash
az signalr key list -n $SIGNALR_NAME -g $GROUP_NAME
```

## Build the Azure Function app

The Azure Function app consists of two key functions. The first is a `negotiate` function that returns the SignalR Service connection information. The second is a `draw` function that is called whenever lines are drawn on any canvas; it will broadcast the lines to the other canvases.

> If you haven't worked with Azure Functions before, check out their [quickstarts](https://docs.microsoft.com/en-us/azure/azure-functions/?WT.mc_id=devto-blog-antchu).

### negotiate function

This HTTP triggered function uses the `SignalRConnectionInfoInput` binding to generate the access token and endpoint information for the client. The binding generates this information using the connection string.

```java
@FunctionName("negotiate")
public SignalRConnectionInfo negotiate(
    @HttpTrigger(
      name = "req",
      methods = { HttpMethod.POST },
      authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> req,
    @SignalRConnectionInfoInput(
      name = "connectionInfo",
      hubName = "serverlessdraw") SignalRConnectionInfo connectionInfo) {
  return connectionInfo;
}
```

### draw function

The `draw` HTTP triggered function uses the `SignalROutput` binding to broadcast the strokes that were drawn on a canvas to all canvases that are currently connected to SignalR Service.

It simply relays the contents of the HTTP request body to SignalR Service.

```java
@FunctionName("draw")
public void draw(
    @HttpTrigger(
      name = "req",
      methods = { HttpMethod.POST },
      authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<StrokeCollection> req,
    @SignalROutput(
      name = "signalRMessage",
      hubName = "serverlessdraw") OutputBinding<SignalRMessage> signalRMessage) {

  StrokeCollection strokeCollection = req.getBody();
  SignalRMessage msg = new SignalRMessage("newStrokes", strokeCollection);
  signalRMessage.setValue(msg);
}
```

## Create the drawing canvas

The canvas is a surprisingly simple browser-based JavaScript app that uses an HTML canvas. You can watch me build the first version of it on [my Twitch stream](https://youtu.be/3sltmfjTRf8).

### SignalR Service connection

The first thing we do when the canvas app starts is connect to SignalR Service. We can reference the SignalR JavaScript client using a CDN.

```html
<script src="https://cdn.jsdelivr.net/npm/@aspnet/signalr@1.1.2/dist/browser/signalr.js"></script>
```

Then we can use the client to create a connection to SignalR Service. By convention, the SignalR client will append `/negotiate` at the end of the URL to discover the connection negotiation endpoint, so we'll leave that part off of our function's URL when we initialize the `HubConnectionBuilder`.

```javascript
var connection = new signalR.HubConnectionBuilder()
  .withUrl(`${apiBaseUrl}/api`) // function URL minus /negotiate
  .build()

// set up event listeners
connection.on('newStrokes', drawStrokes)
connection.on('clearCanvas', clearCanvas)

connection.start()
  .then(() => console.log('connected'))
```

Between configuring the connection and starting it, we set up a couple of event listeners. When an event arrives from SignalR Service that matches one of those names, the event handler will be called. Here's what the `clearCanvas` function looks like:

```javascript
function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
}
```

### The drawing canvas

The main canvas is (you guessed it) a single `<canvas>` tag:

```html
<canvas id="draw-canvas" height="500" width="800"></canvas>
```

The main JavaScript function is named `mouseMove`. It is called when each stroke is drawn. We figure out the start and end of the line, and draw it on the canvas. Then we add the stroke to an array so that we can collect a few strokes together and send them off in a batch.

```javascript
function mouseMove(ev) {

  // 🌟 math happens here

  drawStroke(start, end, colorButton.value)
  unsentStrokes.push({
    start: start,
    end: end,
    color: colorButton.value
  })
}
```

Every 250 milliseconds or so, we post our batch of strokes to our `draw` Azure Function.

```javascript
setInterval(function () {
  if (unsentStrokes.length) {
    axios.post(`${apiBaseUrl}/api/draw`, {
      sender: clientId,
      strokes: unsentStrokes
    })
    unsentStrokes = []
  }
}, 250)
```

The draw function will use SignalR Service to raise the `newStrokes` event in each running instance of our app. This invokes a function named `drawStrokes` to draw the stroke.

Here's what the finished product looks like:

![Serverless draw app](https://thepracticaldev.s3.amazonaws.com/i/08tjfk0nvynqjlz0l0s9.gif)

Check out the [GitHub repo](https://github.com/anthonychu/serverless-draw) for the source code of the function app in Java, JavaScript, and C#.

## Resources

* [Azure SignalR Service bindings for Azure Functions GA announcement](https://azure.microsoft.com/en-us/blog/real-time-serverless-applications-with-the-signalr-service-bindings-in-azure-functions/?WT.mc_id=devto-blog-antchu)
* [Announcing the general availability of Java support in Azure Functions](https://azure.microsoft.com/en-us/blog/announcing-the-general-availability-of-java-support-in-azure-functions/?WT.mc_id=devto-blog-antchu)
* [Bindings for Azure Functions reference](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-signalr-service?WT.mc_id=devto-blog-antchu)
* [SignalR Service serverless programming guide](https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-overview-azure-functions?WT.mc_id=devto-blog-antchu)
* [Serverless Draw source code](https://github.com/anthonychu/serverless-draw)