Building Real-time Notifications with Azure Web PubSub and Azure Functions
Tags: azure, pubsub, messaging, ai
Published: 2023-12-01 04:31:18

Hello people!!! Today I am writing this blog to share my experience of using the Azure Web Pub-Sub service within Azure Function to send real time messages to my React application.

Many a times, we have a requirement to do some processing asynchronously behind the scenes so that we don't block user's activities on the site.

I came across a similar situation where I was working on a chatbot application. This application has a feature that allows a user to upload a file and then train the file on a specific model and then once the file is trained successfully, the user should be able to query the trained data and get responses from it. Very much similar to what we do in chatgpt.

So the requirement was as below:

1. User uploads a file in the chat box
2. Clicks the send icon
3. File is uploaded to Azure storage
4. User receives a success message saying - "File uploaded successfully"

Now, while the file is getting trained asynchronously, the user should be able to continue his/her conversation with rest of the available data.

So, then when should the user query for the data that he/she just uploaded as a file??? The answer to this is whenever the file is processed successfully behind the scenes, there should be some indication to the user saying that your file is now trained successfully and is ready to serve requests.

Here comes the concept of a PubSub notification. When the file is processed or trained, the PubSub service will publish a message. That is what Pub stands for. And this published message should be subscribed by some service/resource to receive it which is termed as Sub. So **_PubSub_** stands for **_Publish_** and **_Subscribe_**.

## How to Subscribe to a message 

You can use **_WebSocket_** connection from your frontend application to stay connected to your publisher service so that you can receive real time messages.

In my case, the publisher is Azure function and the subscriber is React application.

## Create an Azure Web PubSub service

1. Go to Azure portal
2. Type Web PubSub Service in the search bar
3. Click on the Create button 
4. Select Web PubSub and then add the necessary information and create the service

Below is how the service will look like when created successfully

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15ox68hypfv4el8w6ukr.png)

Go to the Keys section and see that there are connection strings available. The primary connection string is to be used while establishing connection. So keep a note of it. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hocngr4i26poc2yni4s9.png)

## Azure Function

An Azure function is an event driven, serverless compute where you can write less code to save costs and maintain the infrastructure. Read more [here](https://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings?tabs=isolated-process%2Cpython-v2&pivots=programming-language-typescript)

So, now my azure function is responsible to read the file from Azure storage and then pass the file to an API endpoint to train it.

As, the function is event driven, I have to add two functions in my function app. One is the **_Blob Storage Trigger_** which executes whenever a new file is uploaded to the specific storage. And the other is the **_HTTP Trigger_** which helps in keeping the Websocket http connection open from the React app to the Azure function.

You have to create a function app in Azure and then add the functions to that function app.

**Steps to create the function app:**

1. Go to Azure portal
2. Type Function App in the search bar
3. Click Create
4. Add the necessary and required information to create the function app
5. After the app is created, go to Configuration and add a setting for the Web PubSub connection string as below. Here you would need the primary connection string that was created when PubSub service was created.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxh8gx1afykjtev9t3lo.png)

Below is my code structure for Azure function:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/natsllj3dlop3mjat62v.png)

The **_AiTrain_** function is a blob storage trigger function and **_Negotiate_** is the HTTP trigger function. I am using typescript.

## Negotiate function

This function is responsible to return the connection object to the subscriber. So, the index.ts file in this function has below code.

`module.exports = function (context, req, connection) {
    context.res = { body: connection };
    context.done();
};`

And in the function.json file, it has below settings:

```
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "webPubSubConnection",
      "name": "connection",
      "hub": "notification",
      "direction": "in"
    }
  ],
  "scriptFile": "../dist/Negotiate/index.js"
}
```

## AiTrain function

This function is responsible to train the uploaded file and publish a message after success.

```
import { AzureFunction, Context } from '@azure/functions';

const blobTrigger: AzureFunction = async function (
	context: Context,
	myBlob: string
): Promise<void> {
	try {
		context.log('TypeScript Blob trigger function');

		const blobURI = decodeURIComponent(context.bindingData.uri);

		// file processing logic
		
			
		//Azure web pubsub actions
		const actions = {
			actionName: 'sendToAll',
			data: `File processing complete!`,
			dataType: 'text',
		};

		context.bindings.actions = actions;

		// No asynchronous operations, so we can return a resolved Promise
		return Promise.resolve();
		}
	} catch (error: unknown) {
		console.error(error);
	}
};

export default blobTrigger;
```
The function.json file has below code:

```
{
  "bindings": [
    {
      "name": "myBlob",
      "type": "blobTrigger",
      "direction": "in",
      "path": "files/{name}",
      "connection": "dev_STORAGE"
    },
    {
      "type": "webPubSub",
      "name": "actions",
      "hub": "notification",
      "direction": "out"
    }
  ],
  "scriptFile": "../dist/AiTrain/index.js"
}
```
This completes the first part of publishing a message from Azure function.

---

## Subscribe to published messages

In order to subscribe the messages from the React application, all you need to do is establish a Websocket connection in your code.

Below is my react code that establishes a Websocket connection to the azure function.

```
useEffect(() => {
		const connect = async () => {
		const res = await fetch(`${API_URI}/api/negotiate`);
		const { url, accessToken } = await res.json();
		const ws = new WebSocket(url);
		ws.onopen = () => console.log('connected');
		ws.onmessage = (event:any) => {
			console.log(JSON.stringify(event.data));
			//Code Logic
		};
		
		};
		connect();
	}, []);
```
We have to call the negotiate endpoint to keep the React app connected to the Azure function.

And that's all. You can see the console messages to see the event object that has the data sent from the Azure function. You can choose any component to display this data as a notification to the user. 

## Conclusion

In conclusion, Azure Web PubSub when coupled with Azure Functions and a React application opens up exciting possibilities for real-time, scalable, and interactive web experiences. We've explored how seamlessly these components integrate to create a dynamic communication channel between the server and client, enabling instant updates and collaborative features.

Keep experimenting, pushing the boundaries, and discovering new ways to leverage Azure Web PubSub. The possibilities are vast.

I hope this blog has inspired you to dive deeper into the realm of real-time communication and discover the endless possibilities that await. Happy coding!