Enhance Azure Function bindings with Azure Service Bus
How often do you find yourself parsing a message, reading a property, using the value just to make another lookup, maybe fetching data from blob storage? Let's get rid of all this extra code and enhance Azure Functions with the superpowers of Azure Service Bus using attribute injection.
How we don't want it to be
An example of how a message is received and its content used to retrieve other data in many examples could be like this
We receive a message, parses the message, use its content to fetch data. Nothing bad about this or? What if we could make this code just a little slimmer.
To add superpowers, we first need to look at the sender of a message or publisher of a topic. When we create the message/topic for consumers, we can add some additional data.
Prior to version 5.x.x of Microsoft.Azure.WebJobs.Extensions.ServiceBus messages had a property called UserProperties, now it's named ApplicationProperties. Still the same key/value dictionary, just renamed because 🤔. Anyways this is where we want to add additional data, and in this case, we want to add the Id of the user.
From code, this can be achieved with the following snippet.
If we do this from the Azure Portal, we have to use the Service Bus Explorer from within the ServiceBus namespace you're working with. For some reason, the properties are here named Custom Properties. Confused? me too 🙃.
Using Superpowers 🚀
Instead of manually parsing and fetching the blob, this time we add another input blob to the Azure Function.
- We have added a new Blob Input
- Added blob path using ApplicationProperties of ServiceBusReceivedMessage
- Set FileAccess, here as Read
- Set the storage connection
- Defined the received blob as BlockBlobClient
With this setup, we get immediate access to the blob which we want to use further in our code. We also reduced the amount of code we had to write, with the cost of adding one more input binding. In my opinion, the is a more clean setup.
Of course, we're not only limited to blob datatypes. Likewise, we can also use this approach when we have output bindings.
As I mentioned this is not something completely new. The naming and some of the types have just changed compared to earlier versions of the components. Still this can help to reduce some of all the boilerplate code.
Happy coding 👨💻