A TypeScript library that wraps Azure Blob, Table, and Queue Storage with a clean, consistent API. Supports both connection strings and Managed Identity — so it works the same in local dev and production Azure environments.
- Blob Storage: Upload, download, list, and manage blobs with SAS URL support
- Table Storage: Complete CRUD operations for Azure Table Storage
- Queue Storage: Send, receive, and manage queue messages
- Managed Identity Support: Passwordless authentication using Azure Managed Identity (system-assigned and user-assigned)
- SAS URL Support: Generate Shared Access Signature URLs — User Delegation SAS with Managed Identity, Service SAS with connection strings
- TypeScript Support: Full type definitions included
- Error Handling: Built-in error handling and validation
- Installation
- Quick Start
- Blob Storage
- Table Storage
- Queue Storage
- Configuration
- Error Handling
- Best Practices
- API Reference
- Contributing
- License
npm install @akadenia/azure-storage --saveYou can authenticate using either:
- Connection String: Get this from the Azure Portal under your storage account's "Access keys" section
- Managed Identity: For applications running in Azure (recommended for production)
Option 1: Connection string
import { BlobStorage, TableStorage, QueueStorage } from '@akadenia/azure-storage';
const connectionString = "DefaultEndpointsProtocol=https;AccountName=yourstorageaccount;AccountKey=yourkey;EndpointSuffix=core.windows.net";
const blobStorage = new BlobStorage(connectionString);
const tableStorage = new TableStorage(connectionString, 'tableName');
const queueStorage = new QueueStorage(connectionString);Option 2: Managed Identity (recommended for production)
import { BlobStorage, TableStorage, QueueStorage } from '@akadenia/azure-storage';
const blobStorage = new BlobStorage({ accountName: 'yourstorageaccount' });
const tableStorage = new TableStorage({ accountName: 'yourstorageaccount', tableName: 'tableName' });
const queueStorage = new QueueStorage({ accountName: 'yourstorageaccount' });The BlobStorage class provides methods to interact with Azure Blob Storage.
import { BlobStorage } from '@akadenia/azure-storage';
const blobStorage = new BlobStorage(connectionString);// Create a container
const containerCreated = await blobStorage.createContainer('my-container');
console.log('Container created:', containerCreated);
// Delete a container
const containerDeleted = await blobStorage.deleteContainer('my-container');
console.log('Container deleted:', containerDeleted);// Upload data from Buffer
const data = Buffer.from('Hello, Azure Blob Storage!');
const uploaded = await blobStorage.uploadData('my-container', 'my-blob.txt', data, {
blobContentType: 'text/plain'
});
// Upload from stream
import { Readable } from 'stream';
const stream = Readable.from(['Stream data content']);
await blobStorage.uploadStream('my-container', 'stream-blob.txt', stream, {
blobContentType: 'text/plain'
});
// Upload with additional headers
await blobStorage.uploadData('my-container', 'document.pdf', pdfBuffer, {
blobContentType: 'application/pdf',
blobCacheControl: 'max-age=3600',
blobContentEncoding: 'gzip'
});
// Legacy upload method
await blobStorage.upload('my-container', 'file.pdf', buffer, buffer.length, 'application/pdf');// Download blob as Buffer
const blobData = await blobStorage.downloadBlob('my-container', 'my-blob.txt');
console.log('Downloaded content:', blobData.toString());
// Check if blob exists before downloading
const exists = await blobStorage.blobExists('my-container', 'my-blob.txt');
if (exists) {
const data = await blobStorage.downloadBlob('my-container', 'my-blob.txt');
}// List all blobs with a prefix
const blobs = await blobStorage.listBlobs('my-container', 'documents/');
blobs.forEach(blob => {
console.log(`Blob: ${blob.name}, Size: ${blob.properties.contentLength}`);
});const deleted = await blobStorage.deleteBlob('my-container', 'my-blob.txt');
console.log('Blob deleted:', deleted);generateSASUrl supports two SAS token types automatically:
- User Delegation SAS — used with Managed Identity (more secure, no account keys)
- Service SAS — used with connection string authentication
import { BlobPermissions } from '@akadenia/azure-storage';
// Read-only SAS, 1 hour expiry
const sasUrl = await blobStorage.generateSASUrl('my-container', 'my-blob.txt', {
startsOn: new Date(),
expiresOn: new Date(Date.now() + 3600 * 1000),
permissions: [BlobPermissions.READ]
});
console.log('SAS URL:', sasUrl.fullUrlWithSAS);
// Write-enabled container-level SAS
const containerSas = await blobStorage.generateSASUrl('my-container', undefined, {
permissions: [BlobPermissions.ADD, BlobPermissions.WRITE],
expiresOn: new Date(Date.now() + 24 * 3600 * 1000)
});SAS Permissions
import { BlobPermissions } from '@akadenia/azure-storage';
BlobPermissions.READ // "r"
BlobPermissions.WRITE // "w"
BlobPermissions.CREATE // "c"
BlobPermissions.DELETE // "d"
BlobPermissions.ADD // "a"The TableStorage class provides methods to interact with Azure Table Storage.
import { TableStorage, ITableEntity } from '@akadenia/azure-storage';
const tableStorage = new TableStorage(connectionString, 'MyTable');const tableCreated = await tableStorage.createTable();
const tableDeleted = await tableStorage.deleteTable();interface UserEntity extends ITableEntity {
partitionKey: string;
rowKey: string;
name: string;
email: string;
age: number;
isActive: boolean;
}
// Insert
const user: UserEntity = {
partitionKey: 'users',
rowKey: 'user-123',
name: 'John Doe',
email: 'john@example.com',
age: 30,
isActive: true
};
await tableStorage.insert(user);
// Get
const retrievedUser = await tableStorage.get('users', 'user-123');
// Update
user.age = 31;
await tableStorage.update(user);
// Upsert (insert or update)
await tableStorage.upsert(newUser);
// Delete
await tableStorage.delete('users', 'user-123');// List all entities
const allUsers = await tableStorage.list<UserEntity>();
// List with filter
const activeUsers = await tableStorage.list<UserEntity>({
queryOptions: { filter: "isActive eq true and age gt 25" }
});
// Access underlying TableClient for advanced operations
const tableClient = tableStorage.getTableClient();The QueueStorage class provides methods to interact with Azure Queue Storage.
import { QueueStorage } from '@akadenia/azure-storage';
const queueStorage = new QueueStorage(connectionString);
// or with Managed Identity:
const queueStorage = new QueueStorage({ accountName: 'yourstorageaccount' });// Send a string message (base64 encoded by default)
await queueStorage.sendMessage('my-queue', 'Hello, Queue!');
// Send an object (auto JSON stringified + base64 encoded)
await queueStorage.sendMessage('my-queue', { orderNumber: '12345', action: 'process' });
// Send without base64 encoding
await queueStorage.sendMessage('my-queue', 'plain text', false);
// Receive messages
const { receivedMessageItems } = await queueStorage.receiveMessages('my-queue', 5, 30);
// Process and delete
for (const message of receivedMessageItems) {
const decoded = Buffer.from(message.messageText, 'base64').toString('utf-8');
const data = JSON.parse(decoded);
// ... process data ...
await queueStorage.deleteMessage('my-queue', message.messageId, message.popReceipt);
}
// Peek without consuming
const { peekedMessageItems } = await queueStorage.peekMessages('my-queue', 5);await queueStorage.createQueue('my-new-queue');
const exists = await queueStorage.queueExists('my-queue');
const count = await queueStorage.getMessageCount('my-queue');
await queueStorage.clearMessages('my-queue');
await queueStorage.deleteQueue('old-queue');
// Access underlying QueueClient for advanced options
const queueClient = queueStorage.getQueueClient('my-queue');
await queueClient.sendMessage('Custom', {
visibilityTimeoutInSeconds: 30,
timeToLiveInSeconds: 3600
});class OrderProcessor {
private queueStorage: QueueStorage;
constructor(connectionString: string) {
this.queueStorage = new QueueStorage(connectionString);
}
async queueOrder(orderNumber: string): Promise<void> {
await this.queueStorage.sendMessage('orders-to-process', { orderNumber, timestamp: Date.now() });
}
async processOrders(): Promise<void> {
const { receivedMessageItems } = await this.queueStorage.receiveMessages('orders-to-process', 10);
for (const message of receivedMessageItems) {
try {
const decoded = Buffer.from(message.messageText, 'base64').toString('utf-8');
const order = JSON.parse(decoded);
console.log(`Processing order ${order.orderNumber}`);
await this.queueStorage.deleteMessage('orders-to-process', message.messageId, message.popReceipt);
} catch (error) {
console.error('Failed to process message:', error);
// Message will become visible again after visibility timeout
}
}
}
}const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
if (!connectionString) throw new Error('AZURE_STORAGE_CONNECTION_STRING is required');const blobStorage = new BlobStorage("UseDevelopmentStorage=true");
const tableStorage = new TableStorage("UseDevelopmentStorage=true", 'MyTable');
const queueStorage = new QueueStorage("UseDevelopmentStorage=true");System-Assigned (Most Common)
Automatically created when you enable managed identity on your Azure resource — no client ID needed.
const blobStorage = new BlobStorage({ accountName: 'yourstorageaccount' });
const tableStorage = new TableStorage({ accountName: 'yourstorageaccount', tableName: 'MyTable' });
const queueStorage = new QueueStorage({ accountName: 'yourstorageaccount' });User-Assigned
A standalone identity shared across multiple Azure resources. Requires the client ID.
const blobStorage = new BlobStorage({
accountName: 'yourstorageaccount',
managedIdentityClientId: 'your-client-id'
});Environment-Based Configuration
const blobStorage = process.env.NODE_ENV === 'production'
? new BlobStorage({ accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME! })
: new BlobStorage(process.env.AZURE_STORAGE_CONNECTION_STRING!);Note: When using Managed Identity, ensure your Azure resource has the appropriate role assignments:
- Storage Blob Data Contributor for Blob Storage
- Storage Table Data Contributor for Table Storage
- Storage Queue Data Contributor for Queue Storage
try {
const blobData = await blobStorage.downloadBlob('container', 'non-existent.txt');
} catch (error) {
if (error.statusCode === 404) {
console.log('Blob not found');
} else {
console.error('Error downloading blob:', error);
}
}const blobStorage = process.env.NODE_ENV === 'production'
? new BlobStorage({ accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME! })
: new BlobStorage(process.env.AZURE_STORAGE_CONNECTION_STRING!);const sasUrl = await blobStorage.generateSASUrl('container', 'blob', {
permissions: [BlobPermissions.READ],
expiresOn: new Date(Date.now() + 3600 * 1000) // 1 hour max
});async function safeUpload(container: string, name: string, data: Buffer) {
try {
return await blobStorage.uploadData(container, name, data);
} catch (error) {
console.error('Upload failed:', error);
throw error;
}
}async function cleanup() {
try {
await blobStorage.deleteContainer('temp-container');
await tableStorage.deleteTable();
} catch (error) {
console.error('Cleanup failed:', error);
}
}| Method | Description | Returns |
|---|---|---|
createContainer(containerName) |
Creates a container if it doesn't exist | Promise<boolean> |
deleteContainer(containerName) |
Deletes a container | Promise<boolean> |
upload(container, blob, buffer, length, contentType) |
Uploads a buffer (legacy) | Promise<boolean> |
uploadData(container, blob, data, headers?) |
Uploads a Buffer with optional headers | Promise<boolean> |
uploadStream(container, blob, stream, headers?) |
Uploads a readable stream | Promise<boolean> |
downloadBlob(container, blob) |
Downloads a blob as Buffer | Promise<Buffer> |
blobExists(container, blob) |
Checks if a blob exists | Promise<boolean> |
listBlobs(container, prefix) |
Lists blobs with a prefix | Promise<BlobItem[]> |
deleteBlob(container, blob) |
Deletes a blob | Promise<boolean> |
generateSASUrl(container, blob?, options?) |
Generates a SAS URL | Promise<SASUrlComponents> |
| Method | Description | Returns |
|---|---|---|
createTable() |
Creates the table | Promise<boolean> |
deleteTable() |
Deletes the table | Promise<boolean> |
insert(entity) |
Inserts an entity | Promise<boolean> |
update(entity) |
Updates an entity | Promise<boolean> |
upsert(entity) |
Inserts or updates an entity | Promise<boolean> |
get(partitionKey, rowKey) |
Gets an entity | Promise<GetTableEntityResponse> |
delete(partitionKey, rowKey) |
Deletes an entity | Promise<boolean> |
list(options?) |
Lists entities with optional filter | Promise<T[]> |
getTableClient() |
Returns the underlying TableClient | TableClient |
| Method | Description | Returns |
|---|---|---|
getQueueClient(queueName) |
Gets a QueueClient | QueueClient |
sendMessage(queue, message, base64Encode?) |
Sends a message | Promise<QueueSendMessageResponse> |
receiveMessages(queue, maxMessages?, visibilityTimeout?) |
Receives messages | Promise<any> |
deleteMessage(queue, messageId, popReceipt) |
Deletes a message | Promise<void> |
peekMessages(queue, maxMessages?) |
Peeks messages without consuming | Promise<any> |
clearMessages(queue) |
Clears all messages | Promise<void> |
createQueue(queue) |
Creates a queue | Promise<void> |
deleteQueue(queue) |
Deletes a queue | Promise<void> |
queueExists(queue) |
Checks if a queue exists | Promise<boolean> |
getMessageCount(queue) |
Gets approximate message count | Promise<number> |
We welcome contributions! Please feel free to submit a Pull Request.
git clone https://github.com/akadenia/AkadeniaAzureStorage.git
cd AkadeniaAzureStorage
npm install
npm run build
npm run test:with-azurite # requires Azurite running locallyWe follow Conventional Commits. Scope is required.
type(scope): description
Common scopes: blob · table · queue · docs · deps · test · build · ci
Types: feat · fix · docs · style · refactor · test · chore
- Node.js >= 20
- Azure Storage account (or Azurite for local development)
For support, please open an issue on GitHub.