Azure Servicebus TS
MicrosoftOfficialCreated by Microsoft
- Skill
- Tool integration
- Cloud and DevOps
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a comprehensive guide to using the @azure/service-bus SDK for building messaging applications. It covers core operations including sending and receiving messages, managing queues and topics, handling message sessions, implementing dead-letter queues, and managing message lifecycles. The documentation includes best practices for authentication using Microsoft Entra, error handling strategies, and patterns for both pull-based and event-driven messaging architectures.
What it does
- Facilitates point-to-point messaging via queues and fan-out patterns via topics and subscriptions.
- Supports message batching for efficient throughput.
- Provides mechanisms for message settlement, including completion, abandonment, and deferral.
- Enables advanced messaging features like message sessions for ordered processing and scheduled delivery.
- Includes robust error handling patterns for transient and permanent service bus errors.
- Demonstrates secure authentication using Azure Identity credentials.
When to use it
- When building decoupled, event-driven microservices architectures.
- When implementing reliable background job processing or task distribution.
- When requiring ordered message processing using sessions.
- When needing to handle complex messaging scenarios like dead-lettering and message deferral.
- When integrating JavaScript or TypeScript applications with Azure messaging infrastructure.
Advantages
- Native support for TypeScript with strong typing for messaging operations.
- Built-in support for Azure-standard authentication and security practices.
- Comprehensive error handling guidance for production reliability.
- Flexible consumption models including both pull-based and push-based event-driven patterns.
- Efficient resource management through client reuse and batching.
Usage examples
- “How do I send a JSON message to an Azure Service Bus queue using TypeScript?”
- The agent provides a code snippet using ServiceBusClient and ServiceBusSender to send a message with a JSON body and appropriate content type.
- “Show me how to set up an event-driven subscriber for a Service Bus queue.”
- The agent demonstrates using the receiver.subscribe() method with processMessage and processError callbacks for handling incoming messages.
- “How should I handle a MessageLockLost error in my Service Bus application?”
- The agent explains that this is a non-retryable error indicating the lock expired, and provides a pattern for handling it within the error callback.
- “What is the best way to send multiple messages to a queue efficiently?”
- The agent provides a pattern using createMessageBatch() and tryAddMessage() to optimize message throughput.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/microsoft/azure-servicebus-ts/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip azure-servicebus-ts.zip -d .agents/skills
Documentation by Microsoft
Azure Service Bus SDK for TypeScript
Enterprise messaging with queues, topics, and subscriptions.
Installation
npm install @azure/service-bus @azure/identityEnvironment Variables
SERVICEBUS_NAMESPACE=<namespace>.servicebus.windows.net
SERVICEBUS_QUEUE_NAME=my-queue
SERVICEBUS_TOPIC_NAME=my-topic
SERVICEBUS_SUBSCRIPTION_NAME=my-subscription
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in productionAuthentication
import { ServiceBusClient } from "@azure/service-bus";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();
const fullyQualifiedNamespace = process.env.SERVICEBUS_NAMESPACE!;
const client = new ServiceBusClient(fullyQualifiedNamespace, credential);Core Workflow
Send Messages to Queue
const sender = client.createSender("my-queue");
// Single message
await sender.sendMessages({
body: { orderId: "12345", amount: 99.99 },
contentType: "application/json",
});
// Batch messages
const batch = await sender.createMessageBatch();
batch.tryAddMessage({ body: "Message 1" });
batch.tryAddMessage({ body: "Message 2" });
await sender.sendMessages(batch);
await sender.close();Receive Messages from Queue
const receiver = client.createReceiver("my-queue");
// Receive batch
const messages = await receiver.receiveMessages(10, { maxWaitTimeInMs: 5000 });
for (const message of messages) {
console.log(`Received: ${message.body}`);
await receiver.completeMessage(message);
}
await receiver.close();Subscribe to Messages (Event-Driven)
const receiver = client.createReceiver("my-queue");
const subscription = receiver.subscribe({
processMessage: async (message) => {
console.log(`Processing: ${message.body}`);
// Message auto-completed on success
},
processError: async (args) => {
console.error(`Error: ${args.error}`);
},
});
// Stop after some time
setTimeout(async () => {
await subscription.close();
await receiver.close();
}, 60000);Topics and Subscriptions
// Send to topic
const topicSender = client.createSender("my-topic");
await topicSender.sendMessages({
body: { event: "order.created", data: { orderId: "123" } },
applicationProperties: { eventType: "order.created" },
});
// Receive from subscription
const subscriptionReceiver = client.createReceiver("my-topic", "my-subscription");
const messages = await subscriptionReceiver.receiveMessages(10);Message Sessions
// Send session message
const sender = client.createSender("session-queue");
await sender.sendMessages({
body: { step: 1, data: "First step" },
sessionId: "workflow-123",
});
// Receive session messages
const sessionReceiver = await client.acceptSession("session-queue", "workflow-123");
const messages = await sessionReceiver.receiveMessages(10);
// Get/set session state
const state = await sessionReceiver.getSessionState();
await sessionReceiver.setSessionState(Buffer.from(JSON.stringify({ progress: 50 })));
await sessionReceiver.close();Dead-Letter Handling
// Move to dead-letter
await receiver.deadLetterMessage(message, {
deadLetterReason: "Validation failed",
deadLetterErrorDescription: "Missing required field: orderId",
});
// Process dead-letter queue
const dlqReceiver = client.createReceiver("my-queue", { subQueueType: "deadLetter" });
const dlqMessages = await dlqReceiver.receiveMessages(10);
for (const msg of dlqMessages) {
console.log(`DLQ Reason: ${msg.deadLetterReason}`);
// Reprocess or log
await dlqReceiver.completeMessage(msg);
}Scheduled Messages
const sender = client.createSender("my-queue");
// Schedule for future delivery
const scheduledTime = new Date(Date.now() + 60000); // 1 minute from now
const sequenceNumber = await sender.scheduleMessages(
{ body: "Delayed message" },
scheduledTime
);
// Cancel scheduled message
await sender.cancelScheduledMessages(sequenceNumber);Message Deferral
// Defer message for later
await receiver.deferMessage(message);
// Receive deferred message by sequence number
const deferredMessage = await receiver.receiveDeferredMessages(message.sequenceNumber!);
await receiver.completeMessage(deferredMessage[0]);Peek Messages (Non-Destructive)
const receiver = client.createReceiver("my-queue");
// Peek without removing
const peekedMessages = await receiver.peekMessages(10);
for (const msg of peekedMessages) {
console.log(`Peeked: ${msg.body}`);
}Key Types
import {
ServiceBusClient,
ServiceBusSender,
ServiceBusReceiver,
ServiceBusSessionReceiver,
ServiceBusMessage,
ServiceBusReceivedMessage,
ProcessMessageCallback,
ProcessErrorCallback,
} from "@azure/service-bus";Receive Modes
// Peek-Lock (default) - message locked until completed/abandoned
const receiver = client.createReceiver("my-queue", { receiveMode: "peekLock" });
await receiver.completeMessage(message); // Remove from queue
await receiver.abandonMessage(message); // Return to queue
await receiver.deferMessage(message); // Defer for later
await receiver.deadLetterMessage(message); // Move to DLQ
// Receive-and-Delete - message removed immediately
const receiver = client.createReceiver("my-queue", { receiveMode: "receiveAndDelete" });Best Practices
Use Microsoft Entra Token Credential - Use
DefaultAzureCredentialfor local development; useManagedIdentityCredentialorWorkloadIdentityCredentialfor productionReuse clients - Create
ServiceBusClientonce, share across senders/receiversClose resources - Always close senders/receivers when done
Handle errors - Implement
processErrorcallback for subscription receiversUse sessions for ordering - When message order matters within a group
Configure dead-letter - Always handle DLQ messages
Batch sends - Use
createMessageBatch()for multiple messages
Reference Documentation
For detailed patterns, see:
Queues vs Topics Patterns - Queue/topic patterns, sessions, receive modes, message settlement
Error Handling and Reliability - ServiceBusError codes, DLQ handling, lock renewal, graceful shutdown
Files
- ATTRIBUTION.mdAdded by AutoGineer976 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md7.1 KiB
- references/error-handling.md11.3 KiB
- references/queues-topics.md9.5 KiB
references/error-handling.md
Error Handling and Reliability
Comprehensive error handling patterns for @azure/service-bus.
ServiceBusError
All Service Bus errors extend ServiceBusError with a code property:
import { ServiceBusError } from "@azure/service-bus";
try {
await sender.sendMessages(message);
} catch (error) {
if (error instanceof ServiceBusError) {
console.log(`Code: ${error.code}`);
console.log(`Message: ${error.message}`);
console.log(`Retryable: ${error.retryable}`);
handleServiceBusError(error);
}
}Error Codes Reference
| Code | Description | Retryable | Action |
|---|---|---|---|
GeneralError | Unspecified error | Maybe | Log and investigate |
MessagingEntityNotFound | Queue/topic/subscription doesn't exist | No | Check entity name, create entity |
MessageLockLost | Lock expired before settlement | No | Message will be redelivered |
MessageNotFound | Message no longer available | No | Already processed or expired |
MessageSizeExceeded | Message too large (>256KB standard, >100MB premium) | No | Reduce message size or use claim check |
MessagingEntityAlreadyExists | Entity already exists | No | Use existing entity |
MessagingEntityDisabled | Entity is disabled | No | Enable entity in portal |
QuotaExceeded | Namespace quota exceeded | No | Delete messages, upgrade tier |
ServiceBusy | Service temporarily overloaded | Yes | Retry with backoff |
ServiceTimeout | Operation timed out | Yes | Retry with backoff |
ServiceCommunicationProblem | Network/connection issue | Yes | Retry with backoff |
SessionCannotBeLocked | Session locked by another receiver | Yes | Retry or use different session |
SessionLockLost | Session lock expired | No | Re-accept session |
UnauthorizedAccess | Authentication/authorization failed | No | Check credentials/permissions |
Error Handling by Code
function handleServiceBusError(error: ServiceBusError): void {
switch (error.code) {
case "MessagingEntityNotFound":
console.error(`Entity not found: ${error.message}`);
// Create entity or fix configuration
break;
case "MessageLockLost":
console.warn("Message lock lost - will be redelivered");
// No action needed, message returns to queue
break;
case "MessageSizeExceeded":
console.error("Message too large - use claim check pattern");
// Store payload in blob, send reference
break;
case "QuotaExceeded":
console.error("Quota exceeded - namespace is full");
// Alert ops team, consider cleanup or upgrade
break;
case "ServiceBusy":
case "ServiceTimeout":
case "ServiceCommunicationProblem":
if (error.retryable) {
console.warn(`Transient error: ${error.code} - will retry`);
// SDK handles retry automatically
}
break;
case "SessionCannotBeLocked":
console.warn("Session busy - trying another session");
// Use acceptNextSession() instead
break;
case "SessionLockLost":
console.warn("Session lock lost - re-accepting session");
// Re-accept the session
break;
case "UnauthorizedAccess":
console.error("Authorization failed - check credentials");
// Verify RBAC roles or connection string
break;
default:
console.error(`Unexpected error: ${error.code} - ${error.message}`);
}
}ProcessErrorArgs in Subscribe
When using receiver.subscribe(), errors are delivered via processError:
import { ProcessErrorArgs } from "@azure/service-bus";
receiver.subscribe({
processMessage: async (message) => {
// Process message
},
processError: async (args: ProcessErrorArgs) => {
console.error(`Error source: ${args.errorSource}`);
console.error(`Entity path: ${args.entityPath}`);
console.error(`Namespace: ${args.fullyQualifiedNamespace}`);
if (args.error instanceof ServiceBusError) {
console.error(`Code: ${args.error.code}`);
console.error(`Retryable: ${args.error.retryable}`);
}
// Error sources:
// - "receive" - Error receiving messages
// - "processMessageCallback" - Error in your processMessage handler
// - "renewLock" - Error renewing message lock
// - "complete" / "abandon" / "deadLetter" - Settlement errors
switch (args.errorSource) {
case "receive":
console.log("Connection issue - SDK will reconnect");
break;
case "processMessageCallback":
console.log("Bug in message handler - fix code");
break;
case "renewLock":
console.log("Lock renewal failed - message may be redelivered");
break;
}
},
});Dead Letter Queue Handling
Messages that can't be processed go to the dead letter queue (DLQ):
// Create DLQ receiver
const dlqReceiver = client.createReceiver("my-queue", {
subQueueType: "deadLetter",
});
// Process dead letters
const deadLetters = await dlqReceiver.receiveMessages(10);
for (const message of deadLetters) {
console.log(`Dead letter reason: ${message.deadLetterReason}`);
console.log(`Error description: ${message.deadLetterErrorDescription}`);
console.log(`Original body: ${JSON.stringify(message.body)}`);
console.log(`Delivery count: ${message.deliveryCount}`);
console.log(`Enqueued time: ${message.enqueuedTimeUtc}`);
// Analyze and fix the issue
if (canReprocess(message)) {
// Resend to main queue
const sender = client.createSender("my-queue");
await sender.sendMessages({ body: message.body });
await sender.close();
}
// Remove from DLQ
await dlqReceiver.completeMessage(message);
}
await dlqReceiver.close();DLQ for Topics
// DLQ receiver for topic subscription
const dlqReceiver = client.createReceiver(
"my-topic",
"my-subscription",
{ subQueueType: "deadLetter" }
);Automatic Dead Lettering
Messages are automatically dead-lettered when:
deliveryCountexceedsmaxDeliveryCount(default: 10)Message TTL expires (if
deadLetteringOnMessageExpirationis enabled)Subscription filter evaluation fails
Graceful Shutdown
Always close resources in the correct order:
const client = new ServiceBusClient(namespace, credential);
const sender = client.createSender("my-queue");
const receiver = client.createReceiver("my-queue");
// Subscribe to messages
const subscription = receiver.subscribe({
processMessage: async (msg) => { /* ... */ },
processError: async (args) => { /* ... */ },
});
// Graceful shutdown handler
async function shutdown(): Promise<void> {
console.log("Shutting down...");
// 1. Stop receiving new messages
await subscription.close();
// 2. Close receiver (waits for in-flight messages)
await receiver.close();
// 3. Close sender (waits for pending sends)
await sender.close();
// 4. Close client last
await client.close();
console.log("Shutdown complete");
}
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);Lock Renewal
Automatic Lock Renewal (Subscribe)
// subscribe() automatically renews locks
receiver.subscribe({
processMessage: async (message) => {
// Lock is auto-renewed while processing
await longRunningOperation(message.body);
},
processError: async (args) => { /* ... */ },
}, {
// Max time to auto-renew (default: 5 minutes)
maxAutoLockRenewalDurationInMs: 10 * 60 * 1000, // 10 minutes
});Manual Lock Renewal (receiveMessages)
const [message] = await receiver.receiveMessages(1);
// For long processing, manually renew lock
const renewalInterval = setInterval(async () => {
try {
await receiver.renewMessageLock(message);
console.log("Lock renewed");
} catch (error) {
console.error("Lock renewal failed:", error);
clearInterval(renewalInterval);
}
}, 30000); // Renew every 30 seconds
try {
await longRunningOperation(message.body);
await receiver.completeMessage(message);
} finally {
clearInterval(renewalInterval);
}Session Lock Renewal
const sessionReceiver = await client.acceptSession("my-queue", "session-123");
// Auto-renewal for sessions
const sessionReceiver = await client.acceptSession("my-queue", "session-123", {
maxAutoLockRenewalDurationInMs: 10 * 60 * 1000,
});
// Manual renewal
await sessionReceiver.renewSessionLock();Connection Recovery
The SDK automatically handles connection recovery:
// SDK reconnects automatically on transient failures
// No manual reconnection code needed
receiver.subscribe({
processMessage: async (message) => {
// Processing continues after reconnection
},
processError: async (args) => {
if (args.errorSource === "receive") {
// Connection issue - SDK is reconnecting
console.log("Connection lost, SDK reconnecting...");
}
},
});Retry Configuration
Configure retry behavior at client level:
import { ServiceBusClient } from "@azure/service-bus";
const client = new ServiceBusClient(namespace, credential, {
retryOptions: {
maxRetries: 3,
retryDelayInMs: 1000,
maxRetryDelayInMs: 30000,
mode: "Exponential", // or "Fixed"
},
});Idempotent Processing
Design for at-least-once delivery:
receiver.subscribe({
processMessage: async (message) => {
const messageId = message.messageId;
// Check if already processed (use database, Redis, etc.)
if (await isAlreadyProcessed(messageId)) {
console.log(`Duplicate message: ${messageId}`);
return; // Message will be completed
}
// Process message
await processOrder(message.body);
// Mark as processed
await markAsProcessed(messageId);
},
processError: async (args) => { /* ... */ },
});Poison Message Handling
Handle messages that repeatedly fail:
receiver.subscribe({
processMessage: async (message) => {
// Check delivery count
if (message.deliveryCount > 5) {
console.warn(`Message ${message.messageId} failed ${message.deliveryCount} times`);
// Dead letter with reason
await receiver.deadLetterMessage(message, {
deadLetterReason: "MaxRetriesExceeded",
deadLetterErrorDescription: `Failed after ${message.deliveryCount} attempts`,
});
return;
}
try {
await processMessage(message.body);
} catch (error) {
// Abandon to retry
await receiver.abandonMessage(message, {
propertiesToModify: {
lastError: error.message,
lastAttempt: new Date().toISOString(),
},
});
}
},
processError: async (args) => { /* ... */ },
}, {
autoCompleteMessages: false, // Manual settlement
});Best Practices Summary
Always handle errors - Implement
processErrorcallbackUse peek-lock mode - Ensures at-least-once delivery
Design for idempotency - Messages may be delivered multiple times
Monitor dead letter queues - Set up alerts for DLQ messages
Configure appropriate lock duration - Match to processing time
Use auto-lock renewal - For long-running operations
Graceful shutdown - Close resources in correct order
Log error codes - Helps diagnose issues
Set maxDeliveryCount - Prevent infinite retry loops
Handle poison messages - Dead letter after max retries
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills · .github/plugins/azure-sdk-typescript/skills/azure-servicebus-ts · commit 0696600
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-servicebus-ts.zip