* chore: upgrade maintenance scripts to robust PyYAML parsing - Replaces fragile regex frontmatter parsing with PyYAML/yaml library - Ensures multi-line descriptions and complex characters are handled safely - Normalizes quoting and field ordering across all maintenance scripts - Updates validator to strictly enforce description quality * fix: restore and refine truncated skill descriptions - Recovered 223+ truncated descriptions from git history (6.5.0 regression) - Refined long descriptions into concise, complete sentences (<200 chars) - Added missing descriptions for brainstorming and orchestration skills - Manually fixed imagen skill description - Resolved dangling links in competitor-alternatives skill * chore: sync generated registry files and document fixes - Regenerated skills index with normalized forward-slash paths - Updated README and CATALOG to reflect restored descriptions - Documented restoration and script improvements in CHANGELOG.md * fix: restore missing skill and align metadata for full 955 count - Renamed SKILL.MD to SKILL.md in andruia-skill-smith to ensure indexing - Fixed risk level and missing section in andruia-skill-smith - Synchronized all registry files for final 955 skill count * chore(scripts): add cross-platform runners and hermetic test orchestration * fix(scripts): harden utf-8 output and clone target writeability * fix(skills): add missing date metadata for strict validation * chore(index): sync generated metadata dates * fix(catalog): normalize skill paths to prevent CI drift * chore: sync generated registry files * fix: enforce LF line endings for generated registry files
529 lines
13 KiB
Markdown
529 lines
13 KiB
Markdown
---
|
|
name: azure-storage-queue-ts
|
|
description: Azure Queue Storage JavaScript/TypeScript SDK (@azure/storage-queue) for message queue operations. Use for sending, receiving, peeking, and deleting messages in queues.
|
|
risk: unknown
|
|
source: community
|
|
date_added: '2026-02-27'
|
|
---
|
|
|
|
# @azure/storage-queue (TypeScript/JavaScript)
|
|
|
|
SDK for Azure Queue Storage operations — send, receive, peek, and manage messages in queues.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install @azure/storage-queue @azure/identity
|
|
```
|
|
|
|
**Current Version**: 12.x
|
|
**Node.js**: >= 18.0.0
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
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)
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
import { QueueServiceClient } from "@azure/storage-queue";
|
|
|
|
const client = QueueServiceClient.fromConnectionString(
|
|
process.env.AZURE_STORAGE_CONNECTION_STRING!
|
|
);
|
|
```
|
|
|
|
### StorageSharedKeyCredential (Node.js only)
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
const queueClient = client.getQueueClient("my-queue");
|
|
await queueClient.create();
|
|
|
|
// Or create if not exists
|
|
await queueClient.createIfNotExists();
|
|
```
|
|
|
|
### List Queues
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
await queueClient.delete();
|
|
|
|
// Or delete if exists
|
|
await queueClient.deleteIfExists();
|
|
```
|
|
|
|
### Get Queue Properties
|
|
|
|
```typescript
|
|
const properties = await queueClient.getProperties();
|
|
console.log("Approximate message count:", properties.approximateMessagesCount);
|
|
console.log("Metadata:", properties.metadata);
|
|
```
|
|
|
|
### Set Queue Metadata
|
|
|
|
```typescript
|
|
await queueClient.setMetadata({
|
|
department: "engineering",
|
|
priority: "high",
|
|
});
|
|
```
|
|
|
|
## Message Operations
|
|
|
|
### Send Message
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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).
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// After receiving
|
|
const response = await queueClient.receiveMessages();
|
|
const message = response.receivedMessageItems[0];
|
|
|
|
if (message) {
|
|
await queueClient.deleteMessage(message.messageId, message.popReceipt);
|
|
}
|
|
```
|
|
|
|
### Clear All Messages
|
|
|
|
```typescript
|
|
await queueClient.clearMessages();
|
|
```
|
|
|
|
## Message Processing Patterns
|
|
|
|
### Basic Worker Pattern
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
1. **Use DefaultAzureCredential** — Prefer AAD over connection strings/keys
|
|
2. **Always delete after processing** — Prevent duplicate processing
|
|
3. **Handle poison messages** — Move failed messages to a dead-letter queue
|
|
4. **Use appropriate visibility timeout** — Set based on expected processing time
|
|
5. **Extend visibility for long tasks** — Update message to prevent timeout
|
|
6. **Use JSON for structured data** — Serialize objects to JSON strings
|
|
7. **Check dequeueCount** — Detect repeatedly failing messages
|
|
8. **Use batch receive** — Receive multiple messages for efficiency
|
|
|
|
## Platform Differences
|
|
|
|
| Feature | Node.js | Browser |
|
|
|---------|---------|---------|
|
|
| `StorageSharedKeyCredential` | ✅ | ❌ |
|
|
| SAS generation | ✅ | ❌ |
|
|
| DefaultAzureCredential | ✅ | ❌ |
|
|
| Anonymous/SAS access | ✅ | ✅ |
|
|
| All message operations | ✅ | ✅ |
|
|
|
|
## When to Use
|
|
This skill is applicable to execute the workflow or actions described in the overview.
|