Azure Service Bus + Azure Functions: A Beginner's Guide with .NET
Azure Service Bus + Azure Functions - The Beginner-Friendly Way to Understand Them
Suppose your API needs to perform some work that doesn't need to happen immediately.
For example:
- Upload a document to PandaDoc
- Send an email
- Generate a PDF
- Process a large file
- Send a notification
- Call another microservice
- Generate a report
You could do everything inside your API request.
But then the user has to wait for all of that work to finish.
Instead, we can use:
API → Azure Service Bus → Azure Function → Process
That's where Azure Service Bus and Azure Functions become useful.
1. What is Azure Service Bus?
Azure Service Bus is a message broker.
In simple words:
Service Bus provides a reliable place for applications to send and receive messages.
Think of it like a waiting line.
.NET API
|
"Process this document"
↓
+----------------------+
| Azure Service Bus |
| Queue |
+----------------------+
|
Message
↓
Worker / Function
The API doesn't have to perform the actual processing immediately.
It can simply send a message and continue.
2. What is an Azure Function?
Azure Function is a small piece of code that runs when something happens.
For example:
Service Bus message arrives
↓
Azure Function
↓
Process message
A Function can be triggered by many things:
- HTTP request
- Service Bus message
- Timer
- Queue message
- Blob upload
- Event
For this article, we are mainly interested in:
Service Bus → Azure Function
3. Why Do We Need Both?
This is the easiest way to remember it.
Azure Service Bus
Responsible for:
"Carry and deliver the message."
Azure Function
Responsible for:
"Run the code that processes the message."
So:
Service Bus = Message delivery
Azure Function = Message processing
They solve different problems.
4. A Simple Real-World Example
Imagine your application allows users to upload documents.
Without Service Bus:
User
↓
API
↓
Upload document
↓
Call PandaDoc
↓
Process document
↓
Send notification
↓
Return response
The user may have to wait several seconds.
Instead:
User
↓
API
↓
Upload document
↓
Send message to Service Bus
↓
Return response
The background processing happens separately:
Service Bus
↓
Azure Function
|
+--> Process document
|
+--> Upload to PandaDoc
|
+--> Update database
|
+--> Send notification
Now the API doesn't need to wait for the entire operation.
5. What is a Queue?
A queue is one of the most common Service Bus features.
Imagine this:
API
|
| Message 1
| Message 2
| Message 3
↓
+----------------------+
| Service Bus |
| Queue |
+----------------------+
↓ ↓ ↓
M1 M2 M3
Messages wait in the queue until a consumer processes them.
For example:
document-queue
Message 1 → Upload contract.pdf
Message 2 → Upload drawing.pdf
Message 3 → Delete old-file.pdf
Message 4 → Process report.pdf
This is very useful for background processing.
6. Why Not Just Call Another API?
You might ask:
"Why don't I simply call another API?"
You can.
But consider this:
API A
|
| HTTP call
↓
API B
What happens if API B is temporarily unavailable?
The request may fail.
With messaging:
API A
↓
Service Bus
↓
API B / Function
The message can remain in the queue until it can be processed.
This creates looser coupling between applications.
7. Sending a Message from .NET
Install the Azure Service Bus package:
dotnet add package Azure.Messaging.ServiceBus
Create a simple message model:
public class DocumentMessage
{
public int DocumentId { get; set; }
public string FileName { get; set; }
public string Operation { get; set; }
}
Create a Service Bus client:
var client = new ServiceBusClient(connectionString);
var sender = client.CreateSender("document-queue");
Create a message:
var documentMessage = new DocumentMessage
{
DocumentId = 123,
FileName = "contract.pdf",
Operation = "Upload"
};
Serialize and send it:
var json = JsonSerializer.Serialize(documentMessage);
var message = new ServiceBusMessage(json);
await sender.SendMessageAsync(message);
That's it.
The message is now sitting in the queue.
8. What Does the Message Look Like?
Conceptually, the message contains something like:
{
"documentId": 123,
"fileName": "contract.pdf",
"operation": "Upload"
}
The important thing is:
Don't put the actual large file inside the Service Bus message.
For document processing, a better approach is usually:
File
↓
Blob Storage
↓
Service Bus
|
| DocumentId / Blob path
↓
Azure Function
The message can contain a reference:
{
"documentId": 123,
"fileName": "contract.pdf",
"blobPath": "documents/123/contract.pdf",
"operation": "Upload"
}
The Function then retrieves the actual file from storage.
9. Azure Function as a Service Bus Consumer
Now let's create a Function that listens to the queue.
A simplified example:
[Function("ProcessDocument")]
public async Task Run([ServiceBusTrigger("document-queue",Connection = "ServiceBusConnection")]
ServiceBusReceivedMessage message)
{
var document = JsonSerializer.Deserialize<DocumentMessage>(message.Body.ToString());
Console.WriteLine($"Processing {document.FileName}");
// Process document here
}
The important part is:
[ServiceBusTrigger("document-queue")]
It tells the Function:
"Run this Function when a message arrives in this queue."
10. The Complete Flow
Now we have:
.NET API
|
| Send Message
↓
Azure Service Bus
|
| Trigger
↓
Azure Function
|
+-------+-------+
↓ ↓ ↓
Blob Database PandaDoc
Storage
This is a very common cloud architecture.
11. What If the Function Fails?
Suppose the Function receives:
Upload contract.pdf
but PandaDoc is temporarily unavailable.
The processing can fail.
Service Bus supports message retry behavior.
Conceptually:
Service Bus
↓
Function
|
X
Failed
↓
Retry
|
X
Failed
↓
Retry
If the message repeatedly fails, it can eventually be moved to the:
Dead-Letter Queue
Queue
↓
Function
|
X
|
Retry
|
X
|
Retry
|
X
|
↓
Dead-Letter Queue
The dead-letter queue gives you a place to investigate messages that could not be processed successfully.
12. What is a Dead-Letter Queue?
A dead-letter queue is basically:
A place for messages that couldn't be successfully processed.
For example:
document-queue
Message 1 → Success
Message 2 → Success
Message 3 → Failed
Message 4 → Success
Message 5 → Failed repeatedly
↓
Dead-Letter Queue
You can later inspect those messages and determine what went wrong.
13. Queue vs Topic
Service Bus also supports Topics.
This is where things become interesting.
Queue
Usually used when one processing flow needs the message.
Queue
↓
Function
For example:
Upload Document
↓
Document Queue
↓
Document Processor
Topic
A Topic can distribute a message to multiple subscriptions.
Topic
|
+--------+--------+
| |
Notification Audit
Subscription Subscription
| |
↓ ↓
Function Function
For example:
Document Uploaded
↓
Document Topic
|
+----> Notification Service
|
+----> Audit Service
|
+----> Document Service
One event can therefore be consumed by multiple independent services.
14. Service Bus + SignalR
Can Service Bus be used with SignalR?
Yes.
But they have different responsibilities.
Service Bus
|
| Backend messaging
↓
Azure Function
|
| Real-time notification
↓
SignalR
|
↓
Browser
For example:
Document Processing
↓
Service Bus
↓
Azure Function
↓
SignalR
↓
Browser
The Service Bus handles the backend message.
SignalR handles real-time communication with the browser.
15. When Should You Use Service Bus?
Use Service Bus when you need reliable asynchronous communication.
Common examples:
- Document processing
- Email sending
- PDF generation
- Image processing
- Order processing
- Payment workflow events
- Notification processing
- Audit events
- Data synchronization
- Microservice communication
- Import processing
- Export processing
- Report generation
- File conversion
- Background jobs
- Third-party API processing
- Webhook processing
- Workflow steps
- Batch processing
- Long-running operations
16. When Should You Use Azure Functions?
Functions are useful when you want code to run because something happened.
For example:
Blob uploaded
↓
Function
Service Bus message
↓
Function
Timer
↓
Function
HTTP request
↓
Function
So you can think:
Azure Function = "Run this code when this event happens."
17. Service Bus vs Azure Function
| Service | Main Responsibility |
|---|---|
| Azure Service Bus | Store and deliver messages |
| Azure Function | Execute code when triggered |
| SignalR | Real-time client communication |
| Blob Storage | Store files |
| App Service | Host web applications/APIs |
They often work together, rather than replacing each other.
18. A Practical Document Processing Architecture
A real document workflow could look like this:
User
|
↓
.NET API
|
| Upload
↓
Blob Storage
|
| Document reference
↓
Azure Service Bus
|
| Trigger
↓
Azure Function
|
+---------+---------+
| | |
↓ ↓ ↓
PandaDoc Database Processing
|
↓
Processing Done
|
↓
SignalR
|
↓
Browser
The important design principle is:
Store the file in Blob Storage and put the file reference/metadata in Service Bus rather than putting a large file into the message.
19. The Simplest Mental Model
If you are completely new to Azure, remember this:
SERVICE BUS
"I have a message"
↓
+-------------+
| Queue |
+-------------+
↓
AZURE FUNCTION
"I'll process it"
↓
WORK
Or even simpler:
Service Bus carries the message. Function does the work.
20. Final Takeaway
Azure Service Bus and Azure Functions are especially useful when your application needs background or event-driven processing.
Instead of:
API
|
+--> Do everything
|
+--> Wait
|
+--> Return response
you can use:
API
|
+--> Send message
|
+--> Return quickly
|
↓
Service Bus
|
↓
Azure Function
|
↓
Background Work
This approach can give you:
- Better separation of responsibilities
- Asynchronous processing
- Reliable message delivery
- Retry capabilities
- Dead-letter handling
- Better scalability
- Looser coupling between services
The one sentence to remember
Azure Service Bus is the communication/waiting line; Azure Function is the worker that wakes up and processes the message.
Enjoyed this article? Share it with your network!