Building a scalable web app with Azure Functions and SignalR

Azure Functions and SignalR are two powerful tools that can be used together to create a scalable web app. Azure Functions is a serverless compute service that allows you to run your code on-demand, without the need to provision or manage virtual machines. SignalR is a real-time communication framework that enables bi-directional communication between clients and servers. In this tutorial, we will show you how to use Azure Functions and SignalR to build a scalable web app.

Prerequisites

Before we start, you will need to have the following:

  • An Azure account
  • Visual Studio Code installed on your computer
  • Azure Functions Visual Studio Code extension
  • Azure Storage Explorer
  • Node.js and npm installed on your computer

Step 1 – Set up Azure Functions and SignalR

The first step is to set up Azure Functions and SignalR. Follow these steps to create a new Azure Functions app and a SignalR service:

  1. Create a new Azure Functions app:
    • Open the Azure Functions extension in Visual Studio Code
    • Click the “Create New Project” button
    • Enter a project name and select a location to save the project
    • Choose “JavaScript” as the language for your functions
    • Choose “Azure Functions v3” as the version
    • Choose “HTTP trigger” as the template for your first function
  2. Create a new SignalR service:
    • Go to the Azure portal
    • Click the “Create a resource” button
    • Search for “SignalR service” and select it
    • Enter a name for your SignalR service and choose a location
    • Choose “Standard” as the pricing tier
    • Click the “Review + create” button and then “Create”

Step 2 – Set up a front-end client

Next, we need to set up a front-end client that will communicate with our Azure Functions app and SignalR service. We will use a simple HTML file with some JavaScript code to create our front-end client.

  1. Create a new HTML file in your project directory and name it “index.html”.
  2. Add the following code to the file:
<!DOCTYPE html>
<html>
<head>
    <title>Chat</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@microsoft/[email protected]/dist/browser/signalr.min.js"></script>
</head>
<body>
    <h1>Chat</h1>
    <div>
        <input type="text" id="username" placeholder="Enter your username" />
        <button id="join">Join</button>
    </div>
    <div>
        <textarea id="messages" cols="50" rows="10"></textarea>
    </div>
    <div>
        <input type="text" id="message" placeholder="Enter your message" />
        <button id="send">Send</button>
    </div>
    <script>
        // TODO: Add SignalR client code here
    </script>
</body>
</html>

This code creates a simple chat UI with an input field to enter your username, a button to join the chat, a textarea to display all the messages, an input field to enter your message, and a button to send your message.

  1. Add the following code inside the TODO block to create a SignalR client and handle the “join” and “send” button clicks:
const usernameInput = document.getElementById("username");
const joinButton = document.getElementById("join");
const messagesTextarea = document.getElementById("messages");
const messageInput = document.getElementById("message");
const sendButton = document.getElementById("send");

// Create a new SignalR connection
const connection = new signalR.HubConnectionBuilder()
    .withUrl("https://your-signalr-service-name.service.signalr.net/client/?access_token=your-access-token")
    .configureLogging(signalR.LogLevel.Information)
    .build();

// Handle the "join" button click
joinButton.addEventListener("click", async () => {
    const username = usernameInput.value;
    await connection.start();
    connection.invoke("Join", username);
});

// Handle the "send" button click
sendButton.addEventListener("click", async () => {
    const message = messageInput.value;
    const username = usernameInput.value;
    connection.invoke("Send", username, message);
    messageInput.value = "";
});

// Handle incoming messages
connection.on("ReceiveMessage", (username, message) => {
    messagesTextarea.value += `${username}: ${message}n`;
});

This code creates a new SignalR connection, handles the “join” button click by starting the connection and invoking the “Join” method on the server, handles the “send” button click by invoking the “Send” method on the server and clearing the message input field, and handles incoming messages by appending them to the textarea.

Step 3 – Set up Azure Functions

Now that we have our front-end client set up, we need to create some Azure Functions that will handle the “Join” and “Send” methods on the server.

  1. Open the “index.js” file in your project directory and replace the existing code with the following code:
const { CosmosClient } = require("@azure/cosmos");
const { v4: uuidv4 } = require("uuid");
const signalR = require("@microsoft/signalr");

const connectionString = "your-cosmos-db-connection-string";
const containerName = "messages";
const databaseName = "chat";

module.exports = async function (context, req) {
    const client = new CosmosClient(connectionString);
    const database = client.database(databaseName);
    const container = database.container(containerName);

    if (req.query.method === "Join") {
        await join(context, container, req.body);
    } else if (req.query.method === "Send") {
        await send(context, container, req.body);
    } else {
        context.res = {
            status: 400,
            body: "Invalid method"
        };
    }
};

async function join(context, container, payload) {
    const id = uuidv4();
    const username = payload;
    const item = {
        id: id,
        username: username,
        message: "joined the chat"
    };
    await container.items.create(item);

    const connection = new signalR.HubConnectionBuilder()
        .withUrl("https://your-signalr-service-name.service.signalr.net/client/?acce<div>ss_token=your-access-token")
        .configureLogging(signalR.LogLevel.Information)
        .build();

    connection.on("Send", async (username, message) => {
        const id = uuidv4();
        const item = {
            id: id,
            username: username,
            message: message
        };
        await container.items.create(item);

        context.bindings.signalRMessage = {
            target: "ReceiveMessage",
            arguments: [username, message]
        };
    });

    await connection.start();
}

async function send(context, container, payload) {
    const id = uuidv4();
    const { username, message } = payload;
    const item = {
        id: id,
        username: username,
        message: message
    };
    await container.items.create(item);

    context.bindings.signalRMessage = {
        target: "ReceiveMessage",
        arguments: [username, message]
    };
}

This code creates two Azure Functions: one to handle the “Join” method, and one to handle the “Send” method. It also connects to an Azure Cosmos DB instance to store and retrieve chat messages.

The “join” function generates a unique ID for the user, creates a new item in the Cosmos DB container to indicate that the user has joined the chat, creates a new SignalR connection for the user, and listens for incoming messages. When a message is received, the function creates a new item in the Cosmos DB container to store the message, and sends a SignalR message to all connected clients with the “ReceiveMessage” target and the sender’s username and message as arguments.

The “send” function generates a unique ID for the message, creates a new item in the Cosmos DB container to store the message, and sends a SignalR message to all connected clients with the “ReceiveMessage” target and the sender’s username and message as arguments.

Step 4 – Test the web app

Now that we have everything set up, it’s time to test our web app and see if everything works as expected.

  1. Start by running your Azure Functions app locally:
    • Open the “Azure Functions” extension in Visual Studio Code
    • Click the “Run” button or press F5
  2. Navigate to the “index.html” file in your project directory and open it in your web browser.
  3. Enter a unique username and click the “Join” button.

  4. Open a new tab or window and navigate to the “index.html” file again.

  5. Enter a different unique username and click the “Join” button.

  6. Go back to the first tab or window and enter a message in the input field.

  7. Click the “Send” button. You should see the message appear in the textarea in both tabs or windows.

  8. Congratulations! You have created a scalable web app with Azure Functions and SignalR.

Conclusion

In this tutorial, we showed you how to use Azure Functions and SignalR to create a scalable web app. We walked you through the process of setting up Azure Functions and SignalR, creating a simple front-end client, creating Azure Functions to handle the “Join” and “Send” methods, and testing the web app. By leveraging the power of these two tools, you can create a real-time communication app that can scale to millions of users without worry.

Related Post