@azure/storage-queue (TypeScript/JavaScript)
SDK for Azure Queue Storage operations — send, receive, peek, and manage messages in queues.
Installation
npm install @azure/storage-queue @azure/identity
Current Version: 12.x
Node.js: >= 18.0.0
Environment Variables
AZURE_STORAGE_ACCOUNT_NAME=<account-name> AZURE_STORAGE_ACCOUNT_KEY=<account-key>
OR connection string
AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...
Authentication
DefaultAzureCredential (Recommended)
import { QueueServiceClient } from "@azure/storage-queue"; import { DefaultAzureCredential } from "@azure/identity";
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const client = new QueueServiceClient(
https://${accountName}.queue.core.windows.net,
new DefaultAzureCredential()
);
Connection String
import { QueueServiceClient } from "@azure/storage-queue";
const client = QueueServiceClient.fromConnectionString( process.env.AZURE_STORAGE_CONNECTION_STRING! );
StorageSharedKeyCredential (Node.js only)
import { QueueServiceClient, StorageSharedKeyCredential } from "@azure/storage-queue";
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!; const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!;
const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
const client = new QueueServiceClient(
https://${accountName}.queue.core.windows.net,
sharedKeyCredential
);
SAS Token
import { QueueServiceClient } from "@azure/storage-queue";
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!; const sasToken = process.env.AZURE_STORAGE_SAS_TOKEN!;
const client = new QueueServiceClient(
https://${accountName}.queue.core.windows.net${sasToken}
);
Client Hierarchy
QueueServiceClient (account level) └── QueueClient (queue level) └── Messages (send, receive, peek, delete)
Queue Operations
Create Queue
const queueClient = client.getQueueClient("my-queue"); await queueClient.create();
// Or create if not exists await queueClient.createIfNotExists();
List Queues
for await (const queue of client.listQueues()) { console.log(queue.name); }
// With prefix filter for await (const queue of client.listQueues({ prefix: "task-" })) { console.log(queue.name); }
Delete Queue
await queueClient.delete();
// Or delete if exists await queueClient.deleteIfExists();
Get Queue Properties
const properties = await queueClient.getProperties(); console.log("Approximate message count:", properties.approximateMessagesCount); console.log("Metadata:", properties.metadata);
Set Queue Metadata
await queueClient.setMetadata({ department: "engineering", priority: "high", });
Message Operations
Send Message
const queueClient = client.getQueueClient("my-queue");
// Simple message await queueClient.sendMessage("Hello, World!");
// With options await queueClient.sendMessage("Delayed message", { visibilityTimeout: 60, // Hidden for 60 seconds messageTimeToLive: 3600, // Expires in 1 hour });
// JSON message (must be string) const task = { type: "process", data: { id: 123 } }; await queueClient.sendMessage(JSON.stringify(task));
Receive Messages
// Receive up to 32 messages (default: 1) const response = await queueClient.receiveMessages({ numberOfMessages: 10, visibilityTimeout: 30, // 30 seconds to process });
for (const message of response.receivedMessageItems) { console.log("Message ID:", message.messageId); console.log("Content:", message.messageText); console.log("Dequeue Count:", message.dequeueCount); console.log("Pop Receipt:", message.popReceipt);
// Process the message...
// Delete after processing await queueClient.deleteMessage(message.messageId, message.popReceipt); }
Peek Messages
Peek without removing from queue (no visibility timeout).
const response = await queueClient.peekMessages({ numberOfMessages: 5, });
for (const message of response.peekedMessageItems) { console.log("Message ID:", message.messageId); console.log("Content:", message.messageText); // Note: No popReceipt - cannot delete peeked messages }
Update Message
Extend visibility timeout or update content.
// Receive a message const response = await queueClient.receiveMessages(); const message = response.receivedMessageItems[0];
if (message) { // Update content and extend visibility const updateResponse = await queueClient.updateMessage( message.messageId, message.popReceipt, "Updated content", 60 // New visibility timeout in seconds );
// Use new popReceipt for subsequent operations console.log("New pop receipt:", updateResponse.popReceipt); }
Delete Message
// After receiving const response = await queueClient.receiveMessages(); const message = response.receivedMessageItems[0];
if (message) { await queueClient.deleteMessage(message.messageId, message.popReceipt); }
Clear All Messages
await queueClient.clearMessages();
Message Processing Patterns
Basic Worker Pattern
async function processQueue(queueClient: QueueClient): Promise<void> { while (true) { const response = await queueClient.receiveMessages({ numberOfMessages: 10, visibilityTimeout: 30, });
if (response.receivedMessageItems.length === 0) {
// No messages, wait before polling again
await sleep(5000);
continue;
}
for (const message of response.receivedMessageItems) {
try {
await processMessage(message.messageText);
await queueClient.deleteMessage(message.messageId, message.popReceipt);
} catch (error) {
console.error(`Failed to process message ${message.messageId}:`, error);
// Message will become visible again after timeout
}
}
} }
async function processMessage(content: string): Promise<void> { const task = JSON.parse(content); // Process task... }
function sleep(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); }
Poison Message Handling
const MAX_DEQUEUE_COUNT = 5;
async function processWithPoisonHandling( queueClient: QueueClient, poisonQueueClient: QueueClient ): Promise<void> { const response = await queueClient.receiveMessages({ numberOfMessages: 10, visibilityTimeout: 30, });
for (const message of response.receivedMessageItems) {
if (message.dequeueCount > MAX_DEQUEUE_COUNT) {
// Move to poison queue
await poisonQueueClient.sendMessage(message.messageText);
await queueClient.deleteMessage(message.messageId, message.popReceipt);
console.log(Moved message ${message.messageId} to poison queue);
continue;
}
try {
await processMessage(message.messageText);
await queueClient.deleteMessage(message.messageId, message.popReceipt);
} catch (error) {
console.error(`Processing failed (attempt ${message.dequeueCount}):`, error);
}
} }
Batch Processing with Visibility Extension
async function processBatchWithExtension(queueClient: QueueClient): Promise<void> { const response = await queueClient.receiveMessages({ numberOfMessages: 1, visibilityTimeout: 60, });
const message = response.receivedMessageItems[0]; if (!message) return;
let popReceipt = message.popReceipt;
// Start visibility extension timer const extensionInterval = setInterval(async () => { try { const updateResponse = await queueClient.updateMessage( message.messageId, popReceipt, message.messageText, 60 // Extend by another 60 seconds ); popReceipt = updateResponse.popReceipt; } catch (error) { console.error("Failed to extend visibility:", error); } }, 45000); // Extend every 45 seconds
try { await longRunningProcess(message.messageText); await queueClient.deleteMessage(message.messageId, popReceipt); } finally { clearInterval(extensionInterval); } }
Message Encoding
By default, messages are Base64 encoded. You can customize this:
import { QueueClient } from "@azure/storage-queue";
// Custom encoder/decoder for plain text
const queueClient = new QueueClient(
https://${accountName}.queue.core.windows.net/my-queue,
credential,
{
messageEncoding: "text", // "base64" (default) or "text"
}
);
// Or with custom encoder
const customQueueClient = new QueueClient(
https://${accountName}.queue.core.windows.net/my-queue,
credential,
{
messageEncoding: {
encode: (message: string) => Buffer.from(message).toString("base64"),
decode: (message: string) => Buffer.from(message, "base64").toString(),
},
}
);
SAS Token Generation (Node.js only)
Generate Queue SAS
import { QueueSASPermissions, generateQueueSASQueryParameters, StorageSharedKeyCredential, } from "@azure/storage-queue";
const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
const sasToken = generateQueueSASQueryParameters( { queueName: "my-queue", permissions: QueueSASPermissions.parse("raup"), // read, add, update, process startsOn: new Date(), expiresOn: new Date(Date.now() + 3600 * 1000), // 1 hour }, sharedKeyCredential ).toString();
const sasUrl = https://${accountName}.queue.core.windows.net/my-queue?${sasToken};
Generate Account SAS
import { AccountSASPermissions, AccountSASResourceTypes, AccountSASServices, generateAccountSASQueryParameters, } from "@azure/storage-queue";
const sasToken = generateAccountSASQueryParameters( { services: AccountSASServices.parse("q").toString(), // queue resourceTypes: AccountSASResourceTypes.parse("sco").toString(), permissions: AccountSASPermissions.parse("rwdlacupi"), expiresOn: new Date(Date.now() + 24 * 3600 * 1000), }, sharedKeyCredential ).toString();
Error Handling
import { RestError } from "@azure/storage-queue";
try {
await queueClient.sendMessage("test");
} catch (error) {
if (error instanceof RestError) {
switch (error.statusCode) {
case 404:
console.log("Queue not found");
break;
case 400:
console.log("Bad request - message too large or invalid");
break;
case 403:
console.log("Access denied");
break;
case 409:
console.log("Queue already exists or being deleted");
break;
default:
console.error(Storage error ${error.statusCode}: ${error.message});
}
}
throw error;
}
TypeScript Types Reference
import { // Clients QueueServiceClient, QueueClient,
// Authentication StorageSharedKeyCredential, AnonymousCredential,
// SAS QueueSASPermissions, AccountSASPermissions, AccountSASServices, AccountSASResourceTypes, generateQueueSASQueryParameters, generateAccountSASQueryParameters,
// Messages DequeuedMessageItem, PeekedMessageItem, QueueSendMessageResponse, QueueReceiveMessageResponse, QueueUpdateMessageResponse,
// Queue QueueItem, QueueGetPropertiesResponse,
// Errors RestError, } from "@azure/storage-queue";
Message Limits
Limit Value
Max message size 64 KB
Max visibility timeout 7 days
Max time-to-live 7 days (or -1 for infinite)
Max messages per receive 32
Default visibility timeout 30 seconds
Best Practices
-
Use DefaultAzureCredential — Prefer AAD over connection strings/keys
-
Always delete after processing — Prevent duplicate processing
-
Handle poison messages — Move failed messages to a dead-letter queue
-
Use appropriate visibility timeout — Set based on expected processing time
-
Extend visibility for long tasks — Update message to prevent timeout
-
Use JSON for structured data — Serialize objects to JSON strings
-
Check dequeueCount — Detect repeatedly failing messages
-
Use batch receive — Receive multiple messages for efficiency
Platform Differences
Feature Node.js Browser
StorageSharedKeyCredential
✅ ❌
SAS generation ✅ ❌
DefaultAzureCredential ✅ ❌
Anonymous/SAS access ✅ ✅
All message operations ✅ ✅