Generated banner for Azure Servicebus TS
Generated logo for Azure Servicebus TS

Azure Servicebus TS

MicrosoftOfficialCreated by Microsoft

  • Skill
  • Tool integration
  • Cloud and DevOps
  • MIT
  • No issues found

From the author

Build messaging applications using Azure Service Bus SDK for JavaScript (@azure/service-bus). Use when implementing queues, topics/subscriptions, message sessions, dead-letter handling, or enterprise messaging patterns.
— the description by Microsoft

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

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/microsoft/azure-servicebus-ts/download
  2. Unzip 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/identity

Environment 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 production

Authentication

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

  1. Use Microsoft Entra Token Credential - Use DefaultAzureCredential for local development; use ManagedIdentityCredential or WorkloadIdentityCredential for production

  2. Reuse clients - Create ServiceBusClient once, share across senders/receivers

  3. Close resources - Always close senders/receivers when done

  4. Handle errors - Implement processError callback for subscription receivers

  5. Use sessions for ordering - When message order matters within a group

  6. Configure dead-letter - Always handle DLQ messages

  7. Batch sends - Use createMessageBatch() for multiple messages

Reference Documentation

For detailed patterns, see:

Files

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

CodeDescriptionRetryableAction
GeneralErrorUnspecified errorMaybeLog and investigate
MessagingEntityNotFoundQueue/topic/subscription doesn't existNoCheck entity name, create entity
MessageLockLostLock expired before settlementNoMessage will be redelivered
MessageNotFoundMessage no longer availableNoAlready processed or expired
MessageSizeExceededMessage too large (>256KB standard, >100MB premium)NoReduce message size or use claim check
MessagingEntityAlreadyExistsEntity already existsNoUse existing entity
MessagingEntityDisabledEntity is disabledNoEnable entity in portal
QuotaExceededNamespace quota exceededNoDelete messages, upgrade tier
ServiceBusyService temporarily overloadedYesRetry with backoff
ServiceTimeoutOperation timed outYesRetry with backoff
ServiceCommunicationProblemNetwork/connection issueYesRetry with backoff
SessionCannotBeLockedSession locked by another receiverYesRetry or use different session
SessionLockLostSession lock expiredNoRe-accept session
UnauthorizedAccessAuthentication/authorization failedNoCheck 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:

  • deliveryCount exceeds maxDeliveryCount (default: 10)

  • Message TTL expires (if deadLetteringOnMessageExpiration is 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

  1. Always handle errors - Implement processError callback

  2. Use peek-lock mode - Ensures at-least-once delivery

  3. Design for idempotency - Messages may be delivered multiple times

  4. Monitor dead letter queues - Set up alerts for DLQ messages

  5. Configure appropriate lock duration - Match to processing time

  6. Use auto-lock renewal - For long-running operations

  7. Graceful shutdown - Close resources in correct order

  8. Log error codes - Helps diagnose issues

  9. Set maxDeliveryCount - Prevent infinite retry loops

  10. 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