Azure Functions SignalR Service Bindings

The Azure Functions SignalR Service binding allows you to integrate Azure Functions with Azure SignalR Service to build real-time web applications. This binding provides declarative ways to send messages to connected clients and to handle incoming client connections and messages.

Getting Started

To use the SignalR Service binding, you need to add the SignalR Service extension to your Azure Functions project. You can do this using the Azure Functions Core Tools:


func extensions install --package Microsoft.Azure.WebJobs.Extensions.SignalRService --version 1.x.x
            

You also need to configure your SignalR Service connection string in your local.settings.json or application settings:


{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "AzureSignalRConnectionString": "Endpoint=YOUR_SIGNALR_ENDPOINT;AccessKey=YOUR_ACCESS_KEY;Version=1.0;"
  }
}
            

Output Binding: Sending Messages

The output binding allows your Azure Function to send messages to connected SignalR clients. You can specify the target hub and connection(s) to send messages to.

Example (C#)


using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

public static class SendMessage
{
    [FunctionName("SendMessage")]
    public static IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
        [SignalR(HubName = "chat")] IAsyncCollector signalRMessages)
    {
        string message = req.Query["message"];
        if (string.IsNullOrEmpty(message))
        {
            message = req.Body.ToString();
        }

        if (string.IsNullOrEmpty(message))
        {
            return new BadRequestObjectResult("Please provide a message in the query string or request body.");
        }

        // Send to all clients in the 'chat' hub
        return new OkObjectResult(signalRMessages.AddAsync(new SignalRMessage
        {
            Target = "newMessage",
            Arguments = new[] { message }
        }));
    }
}
            

Example (JavaScript)


module.exports = async function (context, req) {
    const message = (req.query.message || (req.body && req.body.message));

    if (message) {
        context.bindings.signalRMessages = {
            target: "newMessage",
            arguments: [message]
        };
        context.res = {
            status: 200,
            body: "Message sent successfully."
        };
    } else {
        context.res = {
            status: 400,
            body: "Please pass a message in the query string or in the request body"
        };
    }
};
            

SignalR Message Properties

Input Binding: Handling Client Connections and Messages

The SignalR Service binding can also be used as an input binding to trigger a function when a client connects, disconnects, or sends a message.

SignalRConnectionInfo Input Binding

This binding provides negotiation information to clients, allowing them to connect to the SignalR hub.

Example (C#)


using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

public static class Negotiate
{
    [FunctionName("negotiate")]
    public static IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
        [SignalRConnectionInfo(HubName = "chat")] SignalRConnectionInfo connectionInfo)
    {
        return new OkObjectResult(connectionInfo);
    }
}
            

SignalRTrigger Binding

This binding triggers a function based on events from SignalR clients.

Event Types:

Example (C#)


using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

public static class SignalRHandler
{
    [FunctionName("SignalRHandler")]
    public static void Run(
        [SignalRTrigger(HubName = "chat", EventName = "newMessage")] SignalRMessage message,
        [SignalR(HubName = "chat")] IAsyncCollector broadcast,
        [Queue("notifications")] out string notification)
    {
        // Handle incoming message
        string payload = message.Arguments[0].ToString();
        context.log.LogInformation($"Received message: {payload}");

        // Broadcast the message to all clients
        notification = payload; // Send to a queue for other processing
        broadcast.AddAsync(new SignalRMessage
        {
            Target = "newMessage",
            Arguments = new[] { $"Server received: {payload}" }
        });
    }

    [FunctionName("OnConnected")]
    public static void OnConnected(
        [SignalRTrigger(HubName = "chat", EventName = "connected")] InvocationContext invocationContext)
    {
        context.log.LogInformation($"Client connected: {invocationContext.ConnectionId}");
    }

    [FunctionName("OnDisconnected")]
    public static void OnDisconnected(
        [SignalRTrigger(HubName = "chat", EventName = "disconnected")] InvocationContext invocationContext)
    {
        context.log.LogInformation($"Client disconnected: {invocationContext.ConnectionId}");
    }
}
            

Example (JavaScript)


module.exports = async function (context) {
    if (context.bindingData.event === 'newMessage') {
        const message = context.bindingData.message.arguments[0];
        context.log.info(`Received message: ${message}`);

        // Broadcast the message to all clients
        context.bindings.signalRMessages = {
            target: "newMessage",
            arguments: [`Server received: ${message}`]
        };
    } else if (context.bindingData.event === 'connected') {
        context.log.info(`Client connected: ${context.bindingData.connectionId}`);
    } else if (context.bindingData.event === 'disconnected') {
        context.log.info(`Client disconnected: ${context.bindingData.connectionId}`);
    }
};
            

SignalR Trigger Properties

Advanced Scenarios

Targeting Specific Clients

You can send messages to specific clients by providing their connection IDs in the SignalRMessage object:


// C# Example
signalRMessages.AddAsync(new SignalRMessage
{
    Target = "privateMessage",
    Arguments = new[] { "Hello User!", connectionId }, // Send to a specific connectionId
    Sender = connectionId // Or specify the sender if needed
});
            

Using Groups

SignalR Service allows you to add clients to groups and broadcast messages to entire groups.


// C# Example: Adding a client to a group
[FunctionName("AddToGroup")]
public static async Task AddToGroup(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
    [SignalR(HubName = "chat")] IAsyncCollector signalRGroupActions)
{
    string connectionId = req.Query["connectionId"];
    string groupName = req.Query["groupName"];

    await signalRGroupActions.AddAsync(new SignalRGroupAction
    {
        ConnectionId = connectionId,
        Action = "add",
        GroupName = groupName
    });
}

// C# Example: Sending to a group
signalRMessages.AddAsync(new SignalRMessage
{
    Target = "groupMessage",
    Arguments = new[] { "Message for group members!" },
    GroupName = groupName // Specify the group name
});
            
Note: When using the SignalRGroupAction, the Action property can be "add", "remove", or "removeAll".
Tip: Consider using a dedicated function to handle SignalR group management to keep your code organized.
Warning: Be mindful of the scale and performance implications when sending messages to large numbers of clients or many groups.

Summary

Azure Functions SignalR Service bindings provide a powerful and declarative way to build real-time features into your serverless applications. By leveraging these bindings, you can efficiently manage client connections, send messages, and react to client events without managing the complexities of WebSocket connections yourself.